all messages for Emacs-related lists mirrored at yhetil.org
 help / color / mirror / code / Atom feed
* Requesting review before pushing patch to ELPA
@ 2015-02-02  7:49 Jackson Hamilton
  2015-02-02 17:58 ` Stefan Monnier
  2015-02-04 15:13 ` Stefan Monnier
  0 siblings, 2 replies; 20+ messages in thread
From: Jackson Hamilton @ 2015-02-02  7:49 UTC (permalink / raw)
  To: emacs-devel


[-- Attachment #1.1: Type: text/plain, Size: 1353 bytes --]

Hi guys. I'm about to push my package "context-coloring" to ELPA, but
thought it would be a good idea to run my changes by a few other eyes
before doing so.

First, to add my package I ran the following git commands:

git remote add context-coloring
https://github.com/jacksonrayhamilton/context-coloring.git
git subtree add --prefix=packages/context-coloring/ context-coloring master

This resulted in my entire project history being added to the repository,
which included 247 commits. I am not sure if these should be squashed, but
based on the commit log it seems like this subtree approach is how other
people are managing their externally-maintained packages, so I assume it is
okay to add the whole history.

On top of that, I added 2 patches so that I could test ELPA locally. I was
able to install my package via a "local-elpa" as described in the README.
The patches for that are attached in this email.

There is also one big superpatch for the addition of the context-coloring
package. I squashed the 247 commits into one patch file, but note that the
patch file for the addition of the context-coloring patches represents
those 247 commits. It contains all the external elisp and js files needed
for testing and benchmarking, so don't pay those much mind, unless you
don't want them in the repo, in which case let me know.

Thanks,
Jackson

[-- Attachment #1.2: Type: text/html, Size: 1652 bytes --]

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-Squashed-commits.patch --]
[-- Type: text/x-patch; charset=US-ASCII; name="0001-Squashed-commits.patch", Size: 1326616 bytes --]

From d3c9be58e4e97fbf59384acdabb04a33d6a7e996 Mon Sep 17 00:00:00 2001
From: Jackson Ray Hamilton <jackson@jacksonrayhamilton.com>
Date: Sun, 1 Feb 2015 23:42:01 -0800
Subject: [PATCH] Squashed commits

---
 packages/context-coloring/.elpaignore              |    13 +
 packages/context-coloring/.gitignore               |     2 +
 packages/context-coloring/.travis.yml              |    17 +
 packages/context-coloring/Makefile                 |    31 +
 packages/context-coloring/README.md                |   161 +
 .../benchmark/context-coloring-benchmark.el        |   160 +
 .../benchmark/fixtures/.dir-locals.el              |     1 +
 .../benchmark/fixtures/async-0.9.0.js              |  1123 ++
 .../benchmark/fixtures/jquery-2.1.1.js             |  9190 ++++++++++++++
 .../benchmark/fixtures/lodash-2.4.1.js             |  6785 ++++++++++
 .../benchmark/fixtures/mkdirp-0.5.0.js             |    97 +
 .../context-coloring/context-coloring-themes.el    |   122 +
 packages/context-coloring/context-coloring.el      |   518 +
 .../languages/javascript/.jshintrc                 |    24 +
 .../languages/javascript/binaries/scopifier        |    36 +
 .../languages/javascript/libraries/escope.js       |  1117 ++
 .../languages/javascript/libraries/esprima.js      |  3756 ++++++
 .../languages/javascript/libraries/estraverse.js   |   838 ++
 .../languages/javascript/scopifier.js              |   129 +
 packages/context-coloring/libraries/.dir-locals.el |     2 +
 packages/context-coloring/libraries/ert-async.el   |    89 +
 packages/context-coloring/libraries/js2-mode.el    | 12331 +++++++++++++++++++
 packages/context-coloring/scopifier.png            |   Bin 0 -> 2609 bytes
 packages/context-coloring/screenshot.png           |   Bin 0 -> 22006 bytes
 .../context-coloring/test/context-coloring-test.el |   375 +
 .../context-coloring/test/fixtures/block-scopes.js |     6 +
 packages/context-coloring/test/fixtures/catch.js   |     8 +
 .../test/fixtures/comments-and-strings.js          |     3 +
 .../test/fixtures/function-scopes.js               |     5 +
 packages/context-coloring/test/fixtures/global.js  |     3 +
 .../context-coloring/test/fixtures/key-names.js    |     6 +
 .../context-coloring/test/fixtures/key-values.js   |     8 +
 .../test/fixtures/property-lookup.js               |     5 +
 33 files changed, 36961 insertions(+)
 create mode 100644 packages/context-coloring/.elpaignore
 create mode 100644 packages/context-coloring/.gitignore
 create mode 100644 packages/context-coloring/.travis.yml
 create mode 100644 packages/context-coloring/Makefile
 create mode 100644 packages/context-coloring/README.md
 create mode 100644 packages/context-coloring/benchmark/context-coloring-benchmark.el
 create mode 100644 packages/context-coloring/benchmark/fixtures/.dir-locals.el
 create mode 100644 packages/context-coloring/benchmark/fixtures/async-0.9.0.js
 create mode 100644 packages/context-coloring/benchmark/fixtures/jquery-2.1.1.js
 create mode 100644 packages/context-coloring/benchmark/fixtures/lodash-2.4.1.js
 create mode 100644 packages/context-coloring/benchmark/fixtures/mkdirp-0.5.0.js
 create mode 100644 packages/context-coloring/context-coloring-themes.el
 create mode 100644 packages/context-coloring/context-coloring.el
 create mode 100644 packages/context-coloring/languages/javascript/.jshintrc
 create mode 100755 packages/context-coloring/languages/javascript/binaries/scopifier
 create mode 100644 packages/context-coloring/languages/javascript/libraries/escope.js
 create mode 100644 packages/context-coloring/languages/javascript/libraries/esprima.js
 create mode 100644 packages/context-coloring/languages/javascript/libraries/estraverse.js
 create mode 100644 packages/context-coloring/languages/javascript/scopifier.js
 create mode 100644 packages/context-coloring/libraries/.dir-locals.el
 create mode 100644 packages/context-coloring/libraries/ert-async.el
 create mode 100644 packages/context-coloring/libraries/js2-mode.el
 create mode 100644 packages/context-coloring/scopifier.png
 create mode 100644 packages/context-coloring/screenshot.png
 create mode 100644 packages/context-coloring/test/context-coloring-test.el
 create mode 100644 packages/context-coloring/test/fixtures/block-scopes.js
 create mode 100644 packages/context-coloring/test/fixtures/catch.js
 create mode 100644 packages/context-coloring/test/fixtures/comments-and-strings.js
 create mode 100644 packages/context-coloring/test/fixtures/function-scopes.js
 create mode 100644 packages/context-coloring/test/fixtures/global.js
 create mode 100644 packages/context-coloring/test/fixtures/key-names.js
 create mode 100644 packages/context-coloring/test/fixtures/key-values.js
 create mode 100644 packages/context-coloring/test/fixtures/property-lookup.js

diff --git a/packages/context-coloring/.elpaignore b/packages/context-coloring/.elpaignore
new file mode 100644
index 0000000..5b2e6d9
--- /dev/null
+++ b/packages/context-coloring/.elpaignore
@@ -0,0 +1,13 @@
+.elpaignore
+.gitignore
+.jshintrc
+.travis.yml
+Makefile
+README.md
+benchmark
+libraries/.dir-locals.el
+libraries/ert-async.el
+libraries/js2-mode.el
+scopifier.png
+screenshot.png
+test
diff --git a/packages/context-coloring/.gitignore b/packages/context-coloring/.gitignore
new file mode 100644
index 0000000..d9c77ef
--- /dev/null
+++ b/packages/context-coloring/.gitignore
@@ -0,0 +1,2 @@
+*.elc
+/benchmark/logs/
diff --git a/packages/context-coloring/.travis.yml b/packages/context-coloring/.travis.yml
new file mode 100644
index 0000000..a3b3615
--- /dev/null
+++ b/packages/context-coloring/.travis.yml
@@ -0,0 +1,17 @@
+# https://github.com/rolandwalker/emacs-travis
+
+language: emacs-lisp
+
+env:
+  matrix:
+    - EMACS=emacs24
+
+install:
+  - if [ "$EMACS" = "emacs24" ]; then
+        sudo add-apt-repository -y ppa:cassou/emacs &&
+        sudo apt-get update -qq &&
+        sudo apt-get install -qq emacs24 emacs24-el;
+    fi
+
+script:
+  make test EMACS=${EMACS}
diff --git a/packages/context-coloring/Makefile b/packages/context-coloring/Makefile
new file mode 100644
index 0000000..e718601
--- /dev/null
+++ b/packages/context-coloring/Makefile
@@ -0,0 +1,31 @@
+EMACS = emacs
+
+all: clean compile test
+
+bench:
+	${EMACS} -Q \
+	-L . \
+	-L libraries \
+	-l context-coloring \
+	-l benchmark/context-coloring-benchmark \
+	-f context-coloring-benchmark-run
+
+compile:
+	${EMACS} -Q -batch \
+	-L libraries \
+	-f batch-byte-compile *.el libraries/*.el
+
+clean:
+	rm -f *.elc libraries/*.elc
+
+test:
+	${EMACS} -Q -batch \
+	-L . \
+	-L libraries \
+	-l ert \
+	-l ert-async \
+	-l context-coloring \
+	-l test/context-coloring-test.el \
+	-f ert-run-tests-batch-and-exit
+
+.PHONY: all bench compile clean test
diff --git a/packages/context-coloring/README.md b/packages/context-coloring/README.md
new file mode 100644
index 0000000..c31f895
--- /dev/null
+++ b/packages/context-coloring/README.md
@@ -0,0 +1,161 @@
+# Context Coloring [![Build Status](https://travis-ci.org/jacksonrayhamilton/context-coloring.png?branch=develop)](https://travis-ci.org/jacksonrayhamilton/context-coloring)
+
+<p align="center">
+  <img alt="Screenshot of JavaScript code highlighted by context." src="screenshot.png" title="Screenshot">
+</p>
+
+Highlights code according to function context.
+
+- Code in the global scope is one color. Code in functions within the global
+  scope is a different color, and code within such functions is another color,
+  and so on.
+- Identifiers retain the color of the scope in which they are declared.
+
+Lexical scope information at-a-glance can assist a programmer in understanding
+the overall structure of a program. It can help to curb nasty bugs like name
+shadowing. A rainbow can indicate excessive complexity. State change within a
+closure is easily monitored.
+
+By default, Context Coloring still highlights comments and strings
+syntactically. It is still easy to differentiate code from non-code, and strings
+cannot be confused for variables.
+
+This coloring strategy is probably more useful than conventional syntax
+highlighting. Highlighting keywords can help one to detect spelling errors, but
+a [linter][] could also spot those errors, and if integrated with [flycheck][],
+an extra spot opens up in your editing toolbelt.
+
+Give context coloring a try; you may find that it *changes the way you write
+code*.
+
+## Features
+
+- Supported languages: JavaScript
+- Light and dark (customizable) color schemes.
+- Very fast for files under 1000 lines.
+
+## Installation
+
+Requires Emacs 24+.
+
+JavaScript language support requires either [js2-mode][] or
+[Node.js 0.10+][node], respectively.
+
+### ELPA
+
+- `M-x package-refresh-contents RET`
+- `M-x package-install RET context-coloring RET`
+
+### Git
+
+- Clone this repository.
+
+```bash
+cd ~/.emacs.d/
+git clone https://github.com/jacksonrayhamilton/context-coloring.git
+```
+
+- Byte-compile the package for improved speed.
+
+```bash
+cd context-coloring/
+make compile
+```
+
+- Add the following to your `~/.emacs` file:
+
+```lisp
+(add-to-list 'load-path "~/.emacs.d/context-coloring")
+(require 'context-coloring)
+(add-hook 'js-mode-hook 'context-coloring-mode)
+```
+
+## Customizing
+
+Built-in themes are accessible via `context-coloring-load-theme`.  Available
+themes are: `monokai`, `solarized`, `tango` and `zenburn`.
+
+```lisp
+(require 'context-coloring)
+(context-coloring-load-theme 'zenburn)
+```
+
+You can define your own themes, too:
+
+```lisp
+(context-coloring-define-theme
+ 'zenburn
+ :colors '("#DCDCCC"
+           "#93E0E3"
+           "#BFEBBF"
+           "#F0DFAF"
+           "#DFAF8F"
+           "#CC9393"
+           "#DC8CC3"
+           "#94BFF3"
+           "#9FC59F"
+           "#D0BF8F"
+           "#DCA3A3"))
+```
+
+## Extending
+
+To add support for a new language, write a "scopifier" for it, and define a new
+coloring dispatch strategy with `context-coloring-define-dispatch`. Then the
+plugin should handle the rest.
+
+A "scopifier" is a CLI program that reads a buffer's contents from stdin and
+writes a JSON array of numbers to stdout. Every three numbers in the array
+represent a range of color. For instance, if I fed the following string of
+JavaScript code to a scopifier,
+
+```js
+var a = function () {};
+```
+
+then the scopifier would produce the following array:
+
+```js
+[1,24,0,9,23,1]
+```
+
+Where, for every three numbers, the first number is a 1-indexed start [point][],
+the second number is an exclusive end point, and the third number is a scope
+level. The result of applying level 0 coloring to the range &#91;1, 24) and then
+applying level 1 coloring to the range &#91;9, 23) would result in the following
+coloring:
+
+<p align="center">
+  <img alt="Screenshot of ranges &#91;1, 24) and &#91;9, 23)." src="scopifier.png" title="Screenshot">
+</p>
+
+If there is an abstract syntax tree generator for your language, you can walk
+the syntax tree, find variables and scopes, and build their positions and levels
+into an array like the one above.
+
+For example, a Ruby scopifier might be defined and implemented like this:
+
+```lisp
+(context-coloring-define-dispatch
+ 'ruby
+ :modes '(ruby-mode)
+ :executable "ruby"
+ :command "/home/username/scopifier")
+```
+
+```ruby
+#!/usr/bin/env ruby
+def scopifier(code)
+    # Parse code.
+    # Return an array.
+end
+print scopifier ARGF.read
+```
+
+[linter]: http://jshint.com/about/
+[flycheck]: http://www.flycheck.org/
+[zenburn]: http://github.com/bbatsov/zenburn-emacs
+[point]: http://www.gnu.org/software/emacs/manual/html_node/elisp/Point.html
+[js2-mode]: https://github.com/mooz/js2-mode
+[node]: http://nodejs.org/download/
+[load path]: https://www.gnu.org/software/emacs/manual/html_node/emacs/Lisp-Libraries.html
diff --git a/packages/context-coloring/benchmark/context-coloring-benchmark.el b/packages/context-coloring/benchmark/context-coloring-benchmark.el
new file mode 100644
index 0000000..004b66f
--- /dev/null
+++ b/packages/context-coloring/benchmark/context-coloring-benchmark.el
@@ -0,0 +1,160 @@
+;;; benchmark/context-coloring-benchmark.el --- Benchmarks for context coloring. -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2014-2015  Free Software Foundation, Inc.
+
+;; This file is part of GNU Emacs.
+
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+;;; Code:
+
+(defconst context-coloring-benchmark-path
+  (file-name-directory (or load-file-name buffer-file-name))
+  "This file's directory.")
+
+(defun context-coloring-benchmark-resolve-path (path)
+  "Resolve PATH from this file's directory."
+  (expand-file-name path context-coloring-benchmark-path))
+
+(defun context-coloring-benchmark-log-results (result-file fixture)
+  "Log benchmarking results for FIXTURE to RESULT-FILE."
+  (elp-results)
+  (let ((results-buffer (current-buffer)))
+    (with-temp-buffer
+      (insert (concat fixture "\n"))
+      (prepend-to-buffer results-buffer (point-min) (point-max)))
+    (with-temp-buffer
+      (insert "\n")
+      (append-to-buffer results-buffer (point-min) (point-max))))
+  (make-directory (context-coloring-benchmark-resolve-path "./logs") t)
+  (append-to-file nil nil result-file))
+
+(defun context-coloring-benchmark-next-tick (function)
+  "Defer execution of FUNCTION to clear the stack and to ensure
+asynchrony."
+  (run-at-time 0.001 nil function))
+
+(defun context-coloring-benchmark-next (list continue stop)
+  "Run the next test in LIST by calling CONTINUE.  When LIST is
+exhausted, call STOP instead."
+  (if (null list)
+      (context-coloring-benchmark-next-tick stop)
+    (context-coloring-benchmark-next-tick
+     (lambda ()
+       (funcall
+        continue
+        (car list)
+        (lambda ()
+          (context-coloring-benchmark-next (cdr list) continue stop)))))))
+
+(defun context-coloring-benchmark-async (title setup teardown fixtures callback)
+  "Measure the performance of all FIXTURES, calling CALLBACK when
+all are done."
+  (funcall setup)
+  (let ((result-file (context-coloring-benchmark-resolve-path
+                      (format "./logs/results-%s-%s.log"
+                              title (format-time-string "%s")))))
+    (context-coloring-benchmark-next
+     fixtures
+     (lambda (path next)
+       (let ((fixture (context-coloring-benchmark-resolve-path path))
+             advice)
+         (setq
+          advice
+          (let ((count 0))
+            (lambda (original-function)
+              (funcall
+               original-function
+               (lambda ()
+                 (setq count (+ count 1))
+                 ;; Test 5 times.
+                 (if (= count 5)
+                     (progn
+                       (advice-remove 'context-coloring-colorize advice)
+                       (kill-buffer)
+                       (context-coloring-benchmark-log-results
+                        result-file
+                        fixture)
+                       (funcall next))
+                   (funcall 'context-coloring-colorize)))))))
+         (advice-add 'context-coloring-colorize :around advice)
+         (find-file fixture)))
+     (lambda ()
+       (funcall teardown)
+       (if callback (funcall callback))))))
+
+(defconst context-coloring-benchmark-js-fixtures
+  '("./fixtures/jquery-2.1.1.js"
+    "./fixtures/lodash-2.4.1.js"
+    "./fixtures/async-0.9.0.js"
+    "./fixtures/mkdirp-0.5.0.js")
+  "Arbitrary JavaScript files for performance scrutiny.")
+
+(defun context-coloring-benchmark-js-mode-setup ()
+  "Preparation logic for `js-mode'."
+  (add-hook 'js-mode-hook 'context-coloring-mode)
+  (elp-instrument-package "context-coloring-"))
+
+(defun context-coloring-benchmark-js-mode-teardown ()
+  "Cleanup logic for `js-mode'."
+  (remove-hook 'js-mode-hook 'context-coloring-mode))
+
+(defun context-coloring-benchmark-js-mode-run (callback)
+  "Benchmark `js-mode', then call CALLBACK."
+  (context-coloring-benchmark-async
+   "js-mode"
+   'context-coloring-benchmark-js-mode-setup
+   'context-coloring-benchmark-js-mode-teardown
+   context-coloring-benchmark-js-fixtures
+   callback))
+
+(defun context-coloring-benchmark-js2-mode-setup ()
+  "Preparation logic for `js2-mode'."
+  (require 'js2-mode)
+  (setq js2-mode-show-parse-errors nil)
+  (setq js2-mode-show-strict-warnings nil)
+  (add-to-list 'auto-mode-alist '("\\.js\\'" . js2-mode))
+  (add-hook 'js2-mode-hook 'context-coloring-mode)
+  (elp-instrument-package "context-coloring-"))
+
+(defun context-coloring-benchmark-js2-mode-teardown ()
+  "Cleanup logic for `js2-mode'."
+  (remove-hook 'js2-mode-hook 'context-coloring-mode)
+  (setq auto-mode-alist (delete '("\\.js\\'" . js2-mode)
+                                auto-mode-alist))
+  (setq js2-mode-show-strict-warnings t)
+  (setq js2-mode-show-parse-errors t))
+
+(defun context-coloring-benchmark-js2-mode-run (callback)
+  "Benchmark `js2-mode', then call CALLBACK."
+  (context-coloring-benchmark-async
+   "js2-mode"
+   'context-coloring-benchmark-js2-mode-setup
+   'context-coloring-benchmark-js2-mode-teardown
+   context-coloring-benchmark-js-fixtures
+   callback))
+
+(defun context-coloring-benchmark-run ()
+  "Benchmark all modes, then exit."
+  (context-coloring-benchmark-next
+   '(context-coloring-benchmark-js-mode-run
+     context-coloring-benchmark-js2-mode-run)
+   (lambda (function next)
+     (funcall function next))
+   (lambda ()
+     (kill-emacs))))
+
+(provide 'context-coloring-benchmark)
+
+;;; context-coloring-benchmark.el ends here
diff --git a/packages/context-coloring/benchmark/fixtures/.dir-locals.el b/packages/context-coloring/benchmark/fixtures/.dir-locals.el
new file mode 100644
index 0000000..f3d5e97
--- /dev/null
+++ b/packages/context-coloring/benchmark/fixtures/.dir-locals.el
@@ -0,0 +1 @@
+((nil . ((buffer-read-only . t))))
diff --git a/packages/context-coloring/benchmark/fixtures/async-0.9.0.js b/packages/context-coloring/benchmark/fixtures/async-0.9.0.js
new file mode 100644
index 0000000..01e8afc
--- /dev/null
+++ b/packages/context-coloring/benchmark/fixtures/async-0.9.0.js
@@ -0,0 +1,1123 @@
+/*!
+ * async
+ * https://github.com/caolan/async
+ *
+ * Copyright 2010-2014 Caolan McMahon
+ * Released under the MIT license
+ */
+/*jshint onevar: false, indent:4 */
+/*global setImmediate: false, setTimeout: false, console: false */
+(function () {
+
+    var async = {};
+
+    // global on the server, window in the browser
+    var root, previous_async;
+
+    root = this;
+    if (root != null) {
+      previous_async = root.async;
+    }
+
+    async.noConflict = function () {
+        root.async = previous_async;
+        return async;
+    };
+
+    function only_once(fn) {
+        var called = false;
+        return function() {
+            if (called) throw new Error("Callback was already called.");
+            called = true;
+            fn.apply(root, arguments);
+        }
+    }
+
+    //// cross-browser compatiblity functions ////
+
+    var _toString = Object.prototype.toString;
+
+    var _isArray = Array.isArray || function (obj) {
+        return _toString.call(obj) === '[object Array]';
+    };
+
+    var _each = function (arr, iterator) {
+        if (arr.forEach) {
+            return arr.forEach(iterator);
+        }
+        for (var i = 0; i < arr.length; i += 1) {
+            iterator(arr[i], i, arr);
+        }
+    };
+
+    var _map = function (arr, iterator) {
+        if (arr.map) {
+            return arr.map(iterator);
+        }
+        var results = [];
+        _each(arr, function (x, i, a) {
+            results.push(iterator(x, i, a));
+        });
+        return results;
+    };
+
+    var _reduce = function (arr, iterator, memo) {
+        if (arr.reduce) {
+            return arr.reduce(iterator, memo);
+        }
+        _each(arr, function (x, i, a) {
+            memo = iterator(memo, x, i, a);
+        });
+        return memo;
+    };
+
+    var _keys = function (obj) {
+        if (Object.keys) {
+            return Object.keys(obj);
+        }
+        var keys = [];
+        for (var k in obj) {
+            if (obj.hasOwnProperty(k)) {
+                keys.push(k);
+            }
+        }
+        return keys;
+    };
+
+    //// exported async module functions ////
+
+    //// nextTick implementation with browser-compatible fallback ////
+    if (typeof process === 'undefined' || !(process.nextTick)) {
+        if (typeof setImmediate === 'function') {
+            async.nextTick = function (fn) {
+                // not a direct alias for IE10 compatibility
+                setImmediate(fn);
+            };
+            async.setImmediate = async.nextTick;
+        }
+        else {
+            async.nextTick = function (fn) {
+                setTimeout(fn, 0);
+            };
+            async.setImmediate = async.nextTick;
+        }
+    }
+    else {
+        async.nextTick = process.nextTick;
+        if (typeof setImmediate !== 'undefined') {
+            async.setImmediate = function (fn) {
+              // not a direct alias for IE10 compatibility
+              setImmediate(fn);
+            };
+        }
+        else {
+            async.setImmediate = async.nextTick;
+        }
+    }
+
+    async.each = function (arr, iterator, callback) {
+        callback = callback || function () {};
+        if (!arr.length) {
+            return callback();
+        }
+        var completed = 0;
+        _each(arr, function (x) {
+            iterator(x, only_once(done) );
+        });
+        function done(err) {
+          if (err) {
+              callback(err);
+              callback = function () {};
+          }
+          else {
+              completed += 1;
+              if (completed >= arr.length) {
+                  callback();
+              }
+          }
+        }
+    };
+    async.forEach = async.each;
+
+    async.eachSeries = function (arr, iterator, callback) {
+        callback = callback || function () {};
+        if (!arr.length) {
+            return callback();
+        }
+        var completed = 0;
+        var iterate = function () {
+            iterator(arr[completed], function (err) {
+                if (err) {
+                    callback(err);
+                    callback = function () {};
+                }
+                else {
+                    completed += 1;
+                    if (completed >= arr.length) {
+                        callback();
+                    }
+                    else {
+                        iterate();
+                    }
+                }
+            });
+        };
+        iterate();
+    };
+    async.forEachSeries = async.eachSeries;
+
+    async.eachLimit = function (arr, limit, iterator, callback) {
+        var fn = _eachLimit(limit);
+        fn.apply(null, [arr, iterator, callback]);
+    };
+    async.forEachLimit = async.eachLimit;
+
+    var _eachLimit = function (limit) {
+
+        return function (arr, iterator, callback) {
+            callback = callback || function () {};
+            if (!arr.length || limit <= 0) {
+                return callback();
+            }
+            var completed = 0;
+            var started = 0;
+            var running = 0;
+
+            (function replenish () {
+                if (completed >= arr.length) {
+                    return callback();
+                }
+
+                while (running < limit && started < arr.length) {
+                    started += 1;
+                    running += 1;
+                    iterator(arr[started - 1], function (err) {
+                        if (err) {
+                            callback(err);
+                            callback = function () {};
+                        }
+                        else {
+                            completed += 1;
+                            running -= 1;
+                            if (completed >= arr.length) {
+                                callback();
+                            }
+                            else {
+                                replenish();
+                            }
+                        }
+                    });
+                }
+            })();
+        };
+    };
+
+
+    var doParallel = function (fn) {
+        return function () {
+            var args = Array.prototype.slice.call(arguments);
+            return fn.apply(null, [async.each].concat(args));
+        };
+    };
+    var doParallelLimit = function(limit, fn) {
+        return function () {
+            var args = Array.prototype.slice.call(arguments);
+            return fn.apply(null, [_eachLimit(limit)].concat(args));
+        };
+    };
+    var doSeries = function (fn) {
+        return function () {
+            var args = Array.prototype.slice.call(arguments);
+            return fn.apply(null, [async.eachSeries].concat(args));
+        };
+    };
+
+
+    var _asyncMap = function (eachfn, arr, iterator, callback) {
+        arr = _map(arr, function (x, i) {
+            return {index: i, value: x};
+        });
+        if (!callback) {
+            eachfn(arr, function (x, callback) {
+                iterator(x.value, function (err) {
+                    callback(err);
+                });
+            });
+        } else {
+            var results = [];
+            eachfn(arr, function (x, callback) {
+                iterator(x.value, function (err, v) {
+                    results[x.index] = v;
+                    callback(err);
+                });
+            }, function (err) {
+                callback(err, results);
+            });
+        }
+    };
+    async.map = doParallel(_asyncMap);
+    async.mapSeries = doSeries(_asyncMap);
+    async.mapLimit = function (arr, limit, iterator, callback) {
+        return _mapLimit(limit)(arr, iterator, callback);
+    };
+
+    var _mapLimit = function(limit) {
+        return doParallelLimit(limit, _asyncMap);
+    };
+
+    // reduce only has a series version, as doing reduce in parallel won't
+    // work in many situations.
+    async.reduce = function (arr, memo, iterator, callback) {
+        async.eachSeries(arr, function (x, callback) {
+            iterator(memo, x, function (err, v) {
+                memo = v;
+                callback(err);
+            });
+        }, function (err) {
+            callback(err, memo);
+        });
+    };
+    // inject alias
+    async.inject = async.reduce;
+    // foldl alias
+    async.foldl = async.reduce;
+
+    async.reduceRight = function (arr, memo, iterator, callback) {
+        var reversed = _map(arr, function (x) {
+            return x;
+        }).reverse();
+        async.reduce(reversed, memo, iterator, callback);
+    };
+    // foldr alias
+    async.foldr = async.reduceRight;
+
+    var _filter = function (eachfn, arr, iterator, callback) {
+        var results = [];
+        arr = _map(arr, function (x, i) {
+            return {index: i, value: x};
+        });
+        eachfn(arr, function (x, callback) {
+            iterator(x.value, function (v) {
+                if (v) {
+                    results.push(x);
+                }
+                callback();
+            });
+        }, function (err) {
+            callback(_map(results.sort(function (a, b) {
+                return a.index - b.index;
+            }), function (x) {
+                return x.value;
+            }));
+        });
+    };
+    async.filter = doParallel(_filter);
+    async.filterSeries = doSeries(_filter);
+    // select alias
+    async.select = async.filter;
+    async.selectSeries = async.filterSeries;
+
+    var _reject = function (eachfn, arr, iterator, callback) {
+        var results = [];
+        arr = _map(arr, function (x, i) {
+            return {index: i, value: x};
+        });
+        eachfn(arr, function (x, callback) {
+            iterator(x.value, function (v) {
+                if (!v) {
+                    results.push(x);
+                }
+                callback();
+            });
+        }, function (err) {
+            callback(_map(results.sort(function (a, b) {
+                return a.index - b.index;
+            }), function (x) {
+                return x.value;
+            }));
+        });
+    };
+    async.reject = doParallel(_reject);
+    async.rejectSeries = doSeries(_reject);
+
+    var _detect = function (eachfn, arr, iterator, main_callback) {
+        eachfn(arr, function (x, callback) {
+            iterator(x, function (result) {
+                if (result) {
+                    main_callback(x);
+                    main_callback = function () {};
+                }
+                else {
+                    callback();
+                }
+            });
+        }, function (err) {
+            main_callback();
+        });
+    };
+    async.detect = doParallel(_detect);
+    async.detectSeries = doSeries(_detect);
+
+    async.some = function (arr, iterator, main_callback) {
+        async.each(arr, function (x, callback) {
+            iterator(x, function (v) {
+                if (v) {
+                    main_callback(true);
+                    main_callback = function () {};
+                }
+                callback();
+            });
+        }, function (err) {
+            main_callback(false);
+        });
+    };
+    // any alias
+    async.any = async.some;
+
+    async.every = function (arr, iterator, main_callback) {
+        async.each(arr, function (x, callback) {
+            iterator(x, function (v) {
+                if (!v) {
+                    main_callback(false);
+                    main_callback = function () {};
+                }
+                callback();
+            });
+        }, function (err) {
+            main_callback(true);
+        });
+    };
+    // all alias
+    async.all = async.every;
+
+    async.sortBy = function (arr, iterator, callback) {
+        async.map(arr, function (x, callback) {
+            iterator(x, function (err, criteria) {
+                if (err) {
+                    callback(err);
+                }
+                else {
+                    callback(null, {value: x, criteria: criteria});
+                }
+            });
+        }, function (err, results) {
+            if (err) {
+                return callback(err);
+            }
+            else {
+                var fn = function (left, right) {
+                    var a = left.criteria, b = right.criteria;
+                    return a < b ? -1 : a > b ? 1 : 0;
+                };
+                callback(null, _map(results.sort(fn), function (x) {
+                    return x.value;
+                }));
+            }
+        });
+    };
+
+    async.auto = function (tasks, callback) {
+        callback = callback || function () {};
+        var keys = _keys(tasks);
+        var remainingTasks = keys.length
+        if (!remainingTasks) {
+            return callback();
+        }
+
+        var results = {};
+
+        var listeners = [];
+        var addListener = function (fn) {
+            listeners.unshift(fn);
+        };
+        var removeListener = function (fn) {
+            for (var i = 0; i < listeners.length; i += 1) {
+                if (listeners[i] === fn) {
+                    listeners.splice(i, 1);
+                    return;
+                }
+            }
+        };
+        var taskComplete = function () {
+            remainingTasks--
+            _each(listeners.slice(0), function (fn) {
+                fn();
+            });
+        };
+
+        addListener(function () {
+            if (!remainingTasks) {
+                var theCallback = callback;
+                // prevent final callback from calling itself if it errors
+                callback = function () {};
+
+                theCallback(null, results);
+            }
+        });
+
+        _each(keys, function (k) {
+            var task = _isArray(tasks[k]) ? tasks[k]: [tasks[k]];
+            var taskCallback = function (err) {
+                var args = Array.prototype.slice.call(arguments, 1);
+                if (args.length <= 1) {
+                    args = args[0];
+                }
+                if (err) {
+                    var safeResults = {};
+                    _each(_keys(results), function(rkey) {
+                        safeResults[rkey] = results[rkey];
+                    });
+                    safeResults[k] = args;
+                    callback(err, safeResults);
+                    // stop subsequent errors hitting callback multiple times
+                    callback = function () {};
+                }
+                else {
+                    results[k] = args;
+                    async.setImmediate(taskComplete);
+                }
+            };
+            var requires = task.slice(0, Math.abs(task.length - 1)) || [];
+            var ready = function () {
+                return _reduce(requires, function (a, x) {
+                    return (a && results.hasOwnProperty(x));
+                }, true) && !results.hasOwnProperty(k);
+            };
+            if (ready()) {
+                task[task.length - 1](taskCallback, results);
+            }
+            else {
+                var listener = function () {
+                    if (ready()) {
+                        removeListener(listener);
+                        task[task.length - 1](taskCallback, results);
+                    }
+                };
+                addListener(listener);
+            }
+        });
+    };
+
+    async.retry = function(times, task, callback) {
+        var DEFAULT_TIMES = 5;
+        var attempts = [];
+        // Use defaults if times not passed
+        if (typeof times === 'function') {
+            callback = task;
+            task = times;
+            times = DEFAULT_TIMES;
+        }
+        // Make sure times is a number
+        times = parseInt(times, 10) || DEFAULT_TIMES;
+        var wrappedTask = function(wrappedCallback, wrappedResults) {
+            var retryAttempt = function(task, finalAttempt) {
+                return function(seriesCallback) {
+                    task(function(err, result){
+                        seriesCallback(!err || finalAttempt, {err: err, result: result});
+                    }, wrappedResults);
+                };
+            };
+            while (times) {
+                attempts.push(retryAttempt(task, !(times-=1)));
+            }
+            async.series(attempts, function(done, data){
+                data = data[data.length - 1];
+                (wrappedCallback || callback)(data.err, data.result);
+            });
+        }
+        // If a callback is passed, run this as a controll flow
+        return callback ? wrappedTask() : wrappedTask
+    };
+
+    async.waterfall = function (tasks, callback) {
+        callback = callback || function () {};
+        if (!_isArray(tasks)) {
+          var err = new Error('First argument to waterfall must be an array of functions');
+          return callback(err);
+        }
+        if (!tasks.length) {
+            return callback();
+        }
+        var wrapIterator = function (iterator) {
+            return function (err) {
+                if (err) {
+                    callback.apply(null, arguments);
+                    callback = function () {};
+                }
+                else {
+                    var args = Array.prototype.slice.call(arguments, 1);
+                    var next = iterator.next();
+                    if (next) {
+                        args.push(wrapIterator(next));
+                    }
+                    else {
+                        args.push(callback);
+                    }
+                    async.setImmediate(function () {
+                        iterator.apply(null, args);
+                    });
+                }
+            };
+        };
+        wrapIterator(async.iterator(tasks))();
+    };
+
+    var _parallel = function(eachfn, tasks, callback) {
+        callback = callback || function () {};
+        if (_isArray(tasks)) {
+            eachfn.map(tasks, function (fn, callback) {
+                if (fn) {
+                    fn(function (err) {
+                        var args = Array.prototype.slice.call(arguments, 1);
+                        if (args.length <= 1) {
+                            args = args[0];
+                        }
+                        callback.call(null, err, args);
+                    });
+                }
+            }, callback);
+        }
+        else {
+            var results = {};
+            eachfn.each(_keys(tasks), function (k, callback) {
+                tasks[k](function (err) {
+                    var args = Array.prototype.slice.call(arguments, 1);
+                    if (args.length <= 1) {
+                        args = args[0];
+                    }
+                    results[k] = args;
+                    callback(err);
+                });
+            }, function (err) {
+                callback(err, results);
+            });
+        }
+    };
+
+    async.parallel = function (tasks, callback) {
+        _parallel({ map: async.map, each: async.each }, tasks, callback);
+    };
+
+    async.parallelLimit = function(tasks, limit, callback) {
+        _parallel({ map: _mapLimit(limit), each: _eachLimit(limit) }, tasks, callback);
+    };
+
+    async.series = function (tasks, callback) {
+        callback = callback || function () {};
+        if (_isArray(tasks)) {
+            async.mapSeries(tasks, function (fn, callback) {
+                if (fn) {
+                    fn(function (err) {
+                        var args = Array.prototype.slice.call(arguments, 1);
+                        if (args.length <= 1) {
+                            args = args[0];
+                        }
+                        callback.call(null, err, args);
+                    });
+                }
+            }, callback);
+        }
+        else {
+            var results = {};
+            async.eachSeries(_keys(tasks), function (k, callback) {
+                tasks[k](function (err) {
+                    var args = Array.prototype.slice.call(arguments, 1);
+                    if (args.length <= 1) {
+                        args = args[0];
+                    }
+                    results[k] = args;
+                    callback(err);
+                });
+            }, function (err) {
+                callback(err, results);
+            });
+        }
+    };
+
+    async.iterator = function (tasks) {
+        var makeCallback = function (index) {
+            var fn = function () {
+                if (tasks.length) {
+                    tasks[index].apply(null, arguments);
+                }
+                return fn.next();
+            };
+            fn.next = function () {
+                return (index < tasks.length - 1) ? makeCallback(index + 1): null;
+            };
+            return fn;
+        };
+        return makeCallback(0);
+    };
+
+    async.apply = function (fn) {
+        var args = Array.prototype.slice.call(arguments, 1);
+        return function () {
+            return fn.apply(
+                null, args.concat(Array.prototype.slice.call(arguments))
+            );
+        };
+    };
+
+    var _concat = function (eachfn, arr, fn, callback) {
+        var r = [];
+        eachfn(arr, function (x, cb) {
+            fn(x, function (err, y) {
+                r = r.concat(y || []);
+                cb(err);
+            });
+        }, function (err) {
+            callback(err, r);
+        });
+    };
+    async.concat = doParallel(_concat);
+    async.concatSeries = doSeries(_concat);
+
+    async.whilst = function (test, iterator, callback) {
+        if (test()) {
+            iterator(function (err) {
+                if (err) {
+                    return callback(err);
+                }
+                async.whilst(test, iterator, callback);
+            });
+        }
+        else {
+            callback();
+        }
+    };
+
+    async.doWhilst = function (iterator, test, callback) {
+        iterator(function (err) {
+            if (err) {
+                return callback(err);
+            }
+            var args = Array.prototype.slice.call(arguments, 1);
+            if (test.apply(null, args)) {
+                async.doWhilst(iterator, test, callback);
+            }
+            else {
+                callback();
+            }
+        });
+    };
+
+    async.until = function (test, iterator, callback) {
+        if (!test()) {
+            iterator(function (err) {
+                if (err) {
+                    return callback(err);
+                }
+                async.until(test, iterator, callback);
+            });
+        }
+        else {
+            callback();
+        }
+    };
+
+    async.doUntil = function (iterator, test, callback) {
+        iterator(function (err) {
+            if (err) {
+                return callback(err);
+            }
+            var args = Array.prototype.slice.call(arguments, 1);
+            if (!test.apply(null, args)) {
+                async.doUntil(iterator, test, callback);
+            }
+            else {
+                callback();
+            }
+        });
+    };
+
+    async.queue = function (worker, concurrency) {
+        if (concurrency === undefined) {
+            concurrency = 1;
+        }
+        function _insert(q, data, pos, callback) {
+          if (!q.started){
+            q.started = true;
+          }
+          if (!_isArray(data)) {
+              data = [data];
+          }
+          if(data.length == 0) {
+             // call drain immediately if there are no tasks
+             return async.setImmediate(function() {
+                 if (q.drain) {
+                     q.drain();
+                 }
+             });
+          }
+          _each(data, function(task) {
+              var item = {
+                  data: task,
+                  callback: typeof callback === 'function' ? callback : null
+              };
+
+              if (pos) {
+                q.tasks.unshift(item);
+              } else {
+                q.tasks.push(item);
+              }
+
+              if (q.saturated && q.tasks.length === q.concurrency) {
+                  q.saturated();
+              }
+              async.setImmediate(q.process);
+          });
+        }
+
+        var workers = 0;
+        var q = {
+            tasks: [],
+            concurrency: concurrency,
+            saturated: null,
+            empty: null,
+            drain: null,
+            started: false,
+            paused: false,
+            push: function (data, callback) {
+              _insert(q, data, false, callback);
+            },
+            kill: function () {
+              q.drain = null;
+              q.tasks = [];
+            },
+            unshift: function (data, callback) {
+              _insert(q, data, true, callback);
+            },
+            process: function () {
+                if (!q.paused && workers < q.concurrency && q.tasks.length) {
+                    var task = q.tasks.shift();
+                    if (q.empty && q.tasks.length === 0) {
+                        q.empty();
+                    }
+                    workers += 1;
+                    var next = function () {
+                        workers -= 1;
+                        if (task.callback) {
+                            task.callback.apply(task, arguments);
+                        }
+                        if (q.drain && q.tasks.length + workers === 0) {
+                            q.drain();
+                        }
+                        q.process();
+                    };
+                    var cb = only_once(next);
+                    worker(task.data, cb);
+                }
+            },
+            length: function () {
+                return q.tasks.length;
+            },
+            running: function () {
+                return workers;
+            },
+            idle: function() {
+                return q.tasks.length + workers === 0;
+            },
+            pause: function () {
+                if (q.paused === true) { return; }
+                q.paused = true;
+                q.process();
+            },
+            resume: function () {
+                if (q.paused === false) { return; }
+                q.paused = false;
+                q.process();
+            }
+        };
+        return q;
+    };
+    
+    async.priorityQueue = function (worker, concurrency) {
+        
+        function _compareTasks(a, b){
+          return a.priority - b.priority;
+        };
+        
+        function _binarySearch(sequence, item, compare) {
+          var beg = -1,
+              end = sequence.length - 1;
+          while (beg < end) {
+            var mid = beg + ((end - beg + 1) >>> 1);
+            if (compare(item, sequence[mid]) >= 0) {
+              beg = mid;
+            } else {
+              end = mid - 1;
+            }
+          }
+          return beg;
+        }
+        
+        function _insert(q, data, priority, callback) {
+          if (!q.started){
+            q.started = true;
+          }
+          if (!_isArray(data)) {
+              data = [data];
+          }
+          if(data.length == 0) {
+             // call drain immediately if there are no tasks
+             return async.setImmediate(function() {
+                 if (q.drain) {
+                     q.drain();
+                 }
+             });
+          }
+          _each(data, function(task) {
+              var item = {
+                  data: task,
+                  priority: priority,
+                  callback: typeof callback === 'function' ? callback : null
+              };
+              
+              q.tasks.splice(_binarySearch(q.tasks, item, _compareTasks) + 1, 0, item);
+
+              if (q.saturated && q.tasks.length === q.concurrency) {
+                  q.saturated();
+              }
+              async.setImmediate(q.process);
+          });
+        }
+        
+        // Start with a normal queue
+        var q = async.queue(worker, concurrency);
+        
+        // Override push to accept second parameter representing priority
+        q.push = function (data, priority, callback) {
+          _insert(q, data, priority, callback);
+        };
+        
+        // Remove unshift function
+        delete q.unshift;
+
+        return q;
+    };
+
+    async.cargo = function (worker, payload) {
+        var working     = false,
+            tasks       = [];
+
+        var cargo = {
+            tasks: tasks,
+            payload: payload,
+            saturated: null,
+            empty: null,
+            drain: null,
+            drained: true,
+            push: function (data, callback) {
+                if (!_isArray(data)) {
+                    data = [data];
+                }
+                _each(data, function(task) {
+                    tasks.push({
+                        data: task,
+                        callback: typeof callback === 'function' ? callback : null
+                    });
+                    cargo.drained = false;
+                    if (cargo.saturated && tasks.length === payload) {
+                        cargo.saturated();
+                    }
+                });
+                async.setImmediate(cargo.process);
+            },
+            process: function process() {
+                if (working) return;
+                if (tasks.length === 0) {
+                    if(cargo.drain && !cargo.drained) cargo.drain();
+                    cargo.drained = true;
+                    return;
+                }
+
+                var ts = typeof payload === 'number'
+                            ? tasks.splice(0, payload)
+                            : tasks.splice(0, tasks.length);
+
+                var ds = _map(ts, function (task) {
+                    return task.data;
+                });
+
+                if(cargo.empty) cargo.empty();
+                working = true;
+                worker(ds, function () {
+                    working = false;
+
+                    var args = arguments;
+                    _each(ts, function (data) {
+                        if (data.callback) {
+                            data.callback.apply(null, args);
+                        }
+                    });
+
+                    process();
+                });
+            },
+            length: function () {
+                return tasks.length;
+            },
+            running: function () {
+                return working;
+            }
+        };
+        return cargo;
+    };
+
+    var _console_fn = function (name) {
+        return function (fn) {
+            var args = Array.prototype.slice.call(arguments, 1);
+            fn.apply(null, args.concat([function (err) {
+                var args = Array.prototype.slice.call(arguments, 1);
+                if (typeof console !== 'undefined') {
+                    if (err) {
+                        if (console.error) {
+                            console.error(err);
+                        }
+                    }
+                    else if (console[name]) {
+                        _each(args, function (x) {
+                            console[name](x);
+                        });
+                    }
+                }
+            }]));
+        };
+    };
+    async.log = _console_fn('log');
+    async.dir = _console_fn('dir');
+    /*async.info = _console_fn('info');
+    async.warn = _console_fn('warn');
+    async.error = _console_fn('error');*/
+
+    async.memoize = function (fn, hasher) {
+        var memo = {};
+        var queues = {};
+        hasher = hasher || function (x) {
+            return x;
+        };
+        var memoized = function () {
+            var args = Array.prototype.slice.call(arguments);
+            var callback = args.pop();
+            var key = hasher.apply(null, args);
+            if (key in memo) {
+                async.nextTick(function () {
+                    callback.apply(null, memo[key]);
+                });
+            }
+            else if (key in queues) {
+                queues[key].push(callback);
+            }
+            else {
+                queues[key] = [callback];
+                fn.apply(null, args.concat([function () {
+                    memo[key] = arguments;
+                    var q = queues[key];
+                    delete queues[key];
+                    for (var i = 0, l = q.length; i < l; i++) {
+                      q[i].apply(null, arguments);
+                    }
+                }]));
+            }
+        };
+        memoized.memo = memo;
+        memoized.unmemoized = fn;
+        return memoized;
+    };
+
+    async.unmemoize = function (fn) {
+      return function () {
+        return (fn.unmemoized || fn).apply(null, arguments);
+      };
+    };
+
+    async.times = function (count, iterator, callback) {
+        var counter = [];
+        for (var i = 0; i < count; i++) {
+            counter.push(i);
+        }
+        return async.map(counter, iterator, callback);
+    };
+
+    async.timesSeries = function (count, iterator, callback) {
+        var counter = [];
+        for (var i = 0; i < count; i++) {
+            counter.push(i);
+        }
+        return async.mapSeries(counter, iterator, callback);
+    };
+
+    async.seq = function (/* functions... */) {
+        var fns = arguments;
+        return function () {
+            var that = this;
+            var args = Array.prototype.slice.call(arguments);
+            var callback = args.pop();
+            async.reduce(fns, args, function (newargs, fn, cb) {
+                fn.apply(that, newargs.concat([function () {
+                    var err = arguments[0];
+                    var nextargs = Array.prototype.slice.call(arguments, 1);
+                    cb(err, nextargs);
+                }]))
+            },
+            function (err, results) {
+                callback.apply(that, [err].concat(results));
+            });
+        };
+    };
+
+    async.compose = function (/* functions... */) {
+      return async.seq.apply(null, Array.prototype.reverse.call(arguments));
+    };
+
+    var _applyEach = function (eachfn, fns /*args...*/) {
+        var go = function () {
+            var that = this;
+            var args = Array.prototype.slice.call(arguments);
+            var callback = args.pop();
+            return eachfn(fns, function (fn, cb) {
+                fn.apply(that, args.concat([cb]));
+            },
+            callback);
+        };
+        if (arguments.length > 2) {
+            var args = Array.prototype.slice.call(arguments, 2);
+            return go.apply(this, args);
+        }
+        else {
+            return go;
+        }
+    };
+    async.applyEach = doParallel(_applyEach);
+    async.applyEachSeries = doSeries(_applyEach);
+
+    async.forever = function (fn, callback) {
+        function next(err) {
+            if (err) {
+                if (callback) {
+                    return callback(err);
+                }
+                throw err;
+            }
+            fn(next);
+        }
+        next();
+    };
+
+    // Node.js
+    if (typeof module !== 'undefined' && module.exports) {
+        module.exports = async;
+    }
+    // AMD / RequireJS
+    else if (typeof define !== 'undefined' && define.amd) {
+        define([], function () {
+            return async;
+        });
+    }
+    // included directly via <script> tag
+    else {
+        root.async = async;
+    }
+
+}());
diff --git a/packages/context-coloring/benchmark/fixtures/jquery-2.1.1.js b/packages/context-coloring/benchmark/fixtures/jquery-2.1.1.js
new file mode 100644
index 0000000..9f7b3d3
--- /dev/null
+++ b/packages/context-coloring/benchmark/fixtures/jquery-2.1.1.js
@@ -0,0 +1,9190 @@
+/*!
+ * jQuery JavaScript Library v2.1.1
+ * http://jquery.com/
+ *
+ * Includes Sizzle.js
+ * http://sizzlejs.com/
+ *
+ * Copyright 2005, 2014 jQuery Foundation, Inc. and other contributors
+ * Released under the MIT license
+ * http://jquery.org/license
+ *
+ * Date: 2014-05-01T17:11Z
+ */
+
+(function( global, factory ) {
+
+	if ( typeof module === "object" && typeof module.exports === "object" ) {
+		// For CommonJS and CommonJS-like environments where a proper window is present,
+		// execute the factory and get jQuery
+		// For environments that do not inherently posses a window with a document
+		// (such as Node.js), expose a jQuery-making factory as module.exports
+		// This accentuates the need for the creation of a real window
+		// e.g. var jQuery = require("jquery")(window);
+		// See ticket #14549 for more info
+		module.exports = global.document ?
+			factory( global, true ) :
+			function( w ) {
+				if ( !w.document ) {
+					throw new Error( "jQuery requires a window with a document" );
+				}
+				return factory( w );
+			};
+	} else {
+		factory( global );
+	}
+
+// Pass this if window is not defined yet
+}(typeof window !== "undefined" ? window : this, function( window, noGlobal ) {
+
+// Can't do this because several apps including ASP.NET trace
+// the stack via arguments.caller.callee and Firefox dies if
+// you try to trace through "use strict" call chains. (#13335)
+// Support: Firefox 18+
+//
+
+var arr = [];
+
+var slice = arr.slice;
+
+var concat = arr.concat;
+
+var push = arr.push;
+
+var indexOf = arr.indexOf;
+
+var class2type = {};
+
+var toString = class2type.toString;
+
+var hasOwn = class2type.hasOwnProperty;
+
+var support = {};
+
+
+
+var
+	// Use the correct document accordingly with window argument (sandbox)
+	document = window.document,
+
+	version = "2.1.1",
+
+	// Define a local copy of jQuery
+	jQuery = function( selector, context ) {
+		// The jQuery object is actually just the init constructor 'enhanced'
+		// Need init if jQuery is called (just allow error to be thrown if not included)
+		return new jQuery.fn.init( selector, context );
+	},
+
+	// Support: Android<4.1
+	// Make sure we trim BOM and NBSP
+	rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,
+
+	// Matches dashed string for camelizing
+	rmsPrefix = /^-ms-/,
+	rdashAlpha = /-([\da-z])/gi,
+
+	// Used by jQuery.camelCase as callback to replace()
+	fcamelCase = function( all, letter ) {
+		return letter.toUpperCase();
+	};
+
+jQuery.fn = jQuery.prototype = {
+	// The current version of jQuery being used
+	jquery: version,
+
+	constructor: jQuery,
+
+	// Start with an empty selector
+	selector: "",
+
+	// The default length of a jQuery object is 0
+	length: 0,
+
+	toArray: function() {
+		return slice.call( this );
+	},
+
+	// Get the Nth element in the matched element set OR
+	// Get the whole matched element set as a clean array
+	get: function( num ) {
+		return num != null ?
+
+			// Return just the one element from the set
+			( num < 0 ? this[ num + this.length ] : this[ num ] ) :
+
+			// Return all the elements in a clean array
+			slice.call( this );
+	},
+
+	// Take an array of elements and push it onto the stack
+	// (returning the new matched element set)
+	pushStack: function( elems ) {
+
+		// Build a new jQuery matched element set
+		var ret = jQuery.merge( this.constructor(), elems );
+
+		// Add the old object onto the stack (as a reference)
+		ret.prevObject = this;
+		ret.context = this.context;
+
+		// Return the newly-formed element set
+		return ret;
+	},
+
+	// Execute a callback for every element in the matched set.
+	// (You can seed the arguments with an array of args, but this is
+	// only used internally.)
+	each: function( callback, args ) {
+		return jQuery.each( this, callback, args );
+	},
+
+	map: function( callback ) {
+		return this.pushStack( jQuery.map(this, function( elem, i ) {
+			return callback.call( elem, i, elem );
+		}));
+	},
+
+	slice: function() {
+		return this.pushStack( slice.apply( this, arguments ) );
+	},
+
+	first: function() {
+		return this.eq( 0 );
+	},
+
+	last: function() {
+		return this.eq( -1 );
+	},
+
+	eq: function( i ) {
+		var len = this.length,
+			j = +i + ( i < 0 ? len : 0 );
+		return this.pushStack( j >= 0 && j < len ? [ this[j] ] : [] );
+	},
+
+	end: function() {
+		return this.prevObject || this.constructor(null);
+	},
+
+	// For internal use only.
+	// Behaves like an Array's method, not like a jQuery method.
+	push: push,
+	sort: arr.sort,
+	splice: arr.splice
+};
+
+jQuery.extend = jQuery.fn.extend = function() {
+	var options, name, src, copy, copyIsArray, clone,
+		target = arguments[0] || {},
+		i = 1,
+		length = arguments.length,
+		deep = false;
+
+	// Handle a deep copy situation
+	if ( typeof target === "boolean" ) {
+		deep = target;
+
+		// skip the boolean and the target
+		target = arguments[ i ] || {};
+		i++;
+	}
+
+	// Handle case when target is a string or something (possible in deep copy)
+	if ( typeof target !== "object" && !jQuery.isFunction(target) ) {
+		target = {};
+	}
+
+	// extend jQuery itself if only one argument is passed
+	if ( i === length ) {
+		target = this;
+		i--;
+	}
+
+	for ( ; i < length; i++ ) {
+		// Only deal with non-null/undefined values
+		if ( (options = arguments[ i ]) != null ) {
+			// Extend the base object
+			for ( name in options ) {
+				src = target[ name ];
+				copy = options[ name ];
+
+				// Prevent never-ending loop
+				if ( target === copy ) {
+					continue;
+				}
+
+				// Recurse if we're merging plain objects or arrays
+				if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) {
+					if ( copyIsArray ) {
+						copyIsArray = false;
+						clone = src && jQuery.isArray(src) ? src : [];
+
+					} else {
+						clone = src && jQuery.isPlainObject(src) ? src : {};
+					}
+
+					// Never move original objects, clone them
+					target[ name ] = jQuery.extend( deep, clone, copy );
+
+				// Don't bring in undefined values
+				} else if ( copy !== undefined ) {
+					target[ name ] = copy;
+				}
+			}
+		}
+	}
+
+	// Return the modified object
+	return target;
+};
+
+jQuery.extend({
+	// Unique for each copy of jQuery on the page
+	expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ),
+
+	// Assume jQuery is ready without the ready module
+	isReady: true,
+
+	error: function( msg ) {
+		throw new Error( msg );
+	},
+
+	noop: function() {},
+
+	// See test/unit/core.js for details concerning isFunction.
+	// Since version 1.3, DOM methods and functions like alert
+	// aren't supported. They return false on IE (#2968).
+	isFunction: function( obj ) {
+		return jQuery.type(obj) === "function";
+	},
+
+	isArray: Array.isArray,
+
+	isWindow: function( obj ) {
+		return obj != null && obj === obj.window;
+	},
+
+	isNumeric: function( obj ) {
+		// parseFloat NaNs numeric-cast false positives (null|true|false|"")
+		// ...but misinterprets leading-number strings, particularly hex literals ("0x...")
+		// subtraction forces infinities to NaN
+		return !jQuery.isArray( obj ) && obj - parseFloat( obj ) >= 0;
+	},
+
+	isPlainObject: function( obj ) {
+		// Not plain objects:
+		// - Any object or value whose internal [[Class]] property is not "[object Object]"
+		// - DOM nodes
+		// - window
+		if ( jQuery.type( obj ) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) {
+			return false;
+		}
+
+		if ( obj.constructor &&
+				!hasOwn.call( obj.constructor.prototype, "isPrototypeOf" ) ) {
+			return false;
+		}
+
+		// If the function hasn't returned already, we're confident that
+		// |obj| is a plain object, created by {} or constructed with new Object
+		return true;
+	},
+
+	isEmptyObject: function( obj ) {
+		var name;
+		for ( name in obj ) {
+			return false;
+		}
+		return true;
+	},
+
+	type: function( obj ) {
+		if ( obj == null ) {
+			return obj + "";
+		}
+		// Support: Android < 4.0, iOS < 6 (functionish RegExp)
+		return typeof obj === "object" || typeof obj === "function" ?
+			class2type[ toString.call(obj) ] || "object" :
+			typeof obj;
+	},
+
+	// Evaluates a script in a global context
+	globalEval: function( code ) {
+		var script,
+			indirect = eval;
+
+		code = jQuery.trim( code );
+
+		if ( code ) {
+			// If the code includes a valid, prologue position
+			// strict mode pragma, execute code by injecting a
+			// script tag into the document.
+			if ( code.indexOf("use strict") === 1 ) {
+				script = document.createElement("script");
+				script.text = code;
+				document.head.appendChild( script ).parentNode.removeChild( script );
+			} else {
+			// Otherwise, avoid the DOM node creation, insertion
+			// and removal by using an indirect global eval
+				indirect( code );
+			}
+		}
+	},
+
+	// Convert dashed to camelCase; used by the css and data modules
+	// Microsoft forgot to hump their vendor prefix (#9572)
+	camelCase: function( string ) {
+		return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase );
+	},
+
+	nodeName: function( elem, name ) {
+		return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase();
+	},
+
+	// args is for internal usage only
+	each: function( obj, callback, args ) {
+		var value,
+			i = 0,
+			length = obj.length,
+			isArray = isArraylike( obj );
+
+		if ( args ) {
+			if ( isArray ) {
+				for ( ; i < length; i++ ) {
+					value = callback.apply( obj[ i ], args );
+
+					if ( value === false ) {
+						break;
+					}
+				}
+			} else {
+				for ( i in obj ) {
+					value = callback.apply( obj[ i ], args );
+
+					if ( value === false ) {
+						break;
+					}
+				}
+			}
+
+		// A special, fast, case for the most common use of each
+		} else {
+			if ( isArray ) {
+				for ( ; i < length; i++ ) {
+					value = callback.call( obj[ i ], i, obj[ i ] );
+
+					if ( value === false ) {
+						break;
+					}
+				}
+			} else {
+				for ( i in obj ) {
+					value = callback.call( obj[ i ], i, obj[ i ] );
+
+					if ( value === false ) {
+						break;
+					}
+				}
+			}
+		}
+
+		return obj;
+	},
+
+	// Support: Android<4.1
+	trim: function( text ) {
+		return text == null ?
+			"" :
+			( text + "" ).replace( rtrim, "" );
+	},
+
+	// results is for internal usage only
+	makeArray: function( arr, results ) {
+		var ret = results || [];
+
+		if ( arr != null ) {
+			if ( isArraylike( Object(arr) ) ) {
+				jQuery.merge( ret,
+					typeof arr === "string" ?
+					[ arr ] : arr
+				);
+			} else {
+				push.call( ret, arr );
+			}
+		}
+
+		return ret;
+	},
+
+	inArray: function( elem, arr, i ) {
+		return arr == null ? -1 : indexOf.call( arr, elem, i );
+	},
+
+	merge: function( first, second ) {
+		var len = +second.length,
+			j = 0,
+			i = first.length;
+
+		for ( ; j < len; j++ ) {
+			first[ i++ ] = second[ j ];
+		}
+
+		first.length = i;
+
+		return first;
+	},
+
+	grep: function( elems, callback, invert ) {
+		var callbackInverse,
+			matches = [],
+			i = 0,
+			length = elems.length,
+			callbackExpect = !invert;
+
+		// Go through the array, only saving the items
+		// that pass the validator function
+		for ( ; i < length; i++ ) {
+			callbackInverse = !callback( elems[ i ], i );
+			if ( callbackInverse !== callbackExpect ) {
+				matches.push( elems[ i ] );
+			}
+		}
+
+		return matches;
+	},
+
+	// arg is for internal usage only
+	map: function( elems, callback, arg ) {
+		var value,
+			i = 0,
+			length = elems.length,
+			isArray = isArraylike( elems ),
+			ret = [];
+
+		// Go through the array, translating each of the items to their new values
+		if ( isArray ) {
+			for ( ; i < length; i++ ) {
+				value = callback( elems[ i ], i, arg );
+
+				if ( value != null ) {
+					ret.push( value );
+				}
+			}
+
+		// Go through every key on the object,
+		} else {
+			for ( i in elems ) {
+				value = callback( elems[ i ], i, arg );
+
+				if ( value != null ) {
+					ret.push( value );
+				}
+			}
+		}
+
+		// Flatten any nested arrays
+		return concat.apply( [], ret );
+	},
+
+	// A global GUID counter for objects
+	guid: 1,
+
+	// Bind a function to a context, optionally partially applying any
+	// arguments.
+	proxy: function( fn, context ) {
+		var tmp, args, proxy;
+
+		if ( typeof context === "string" ) {
+			tmp = fn[ context ];
+			context = fn;
+			fn = tmp;
+		}
+
+		// Quick check to determine if target is callable, in the spec
+		// this throws a TypeError, but we will just return undefined.
+		if ( !jQuery.isFunction( fn ) ) {
+			return undefined;
+		}
+
+		// Simulated bind
+		args = slice.call( arguments, 2 );
+		proxy = function() {
+			return fn.apply( context || this, args.concat( slice.call( arguments ) ) );
+		};
+
+		// Set the guid of unique handler to the same of original handler, so it can be removed
+		proxy.guid = fn.guid = fn.guid || jQuery.guid++;
+
+		return proxy;
+	},
+
+	now: Date.now,
+
+	// jQuery.support is not used in Core but other projects attach their
+	// properties to it so it needs to exist.
+	support: support
+});
+
+// Populate the class2type map
+jQuery.each("Boolean Number String Function Array Date RegExp Object Error".split(" "), function(i, name) {
+	class2type[ "[object " + name + "]" ] = name.toLowerCase();
+});
+
+function isArraylike( obj ) {
+	var length = obj.length,
+		type = jQuery.type( obj );
+
+	if ( type === "function" || jQuery.isWindow( obj ) ) {
+		return false;
+	}
+
+	if ( obj.nodeType === 1 && length ) {
+		return true;
+	}
+
+	return type === "array" || length === 0 ||
+		typeof length === "number" && length > 0 && ( length - 1 ) in obj;
+}
+var Sizzle =
+/*!
+ * Sizzle CSS Selector Engine v1.10.19
+ * http://sizzlejs.com/
+ *
+ * Copyright 2013 jQuery Foundation, Inc. and other contributors
+ * Released under the MIT license
+ * http://jquery.org/license
+ *
+ * Date: 2014-04-18
+ */
+(function( window ) {
+
+var i,
+	support,
+	Expr,
+	getText,
+	isXML,
+	tokenize,
+	compile,
+	select,
+	outermostContext,
+	sortInput,
+	hasDuplicate,
+
+	// Local document vars
+	setDocument,
+	document,
+	docElem,
+	documentIsHTML,
+	rbuggyQSA,
+	rbuggyMatches,
+	matches,
+	contains,
+
+	// Instance-specific data
+	expando = "sizzle" + -(new Date()),
+	preferredDoc = window.document,
+	dirruns = 0,
+	done = 0,
+	classCache = createCache(),
+	tokenCache = createCache(),
+	compilerCache = createCache(),
+	sortOrder = function( a, b ) {
+		if ( a === b ) {
+			hasDuplicate = true;
+		}
+		return 0;
+	},
+
+	// General-purpose constants
+	strundefined = typeof undefined,
+	MAX_NEGATIVE = 1 << 31,
+
+	// Instance methods
+	hasOwn = ({}).hasOwnProperty,
+	arr = [],
+	pop = arr.pop,
+	push_native = arr.push,
+	push = arr.push,
+	slice = arr.slice,
+	// Use a stripped-down indexOf if we can't use a native one
+	indexOf = arr.indexOf || function( elem ) {
+		var i = 0,
+			len = this.length;
+		for ( ; i < len; i++ ) {
+			if ( this[i] === elem ) {
+				return i;
+			}
+		}
+		return -1;
+	},
+
+	booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",
+
+	// Regular expressions
+
+	// Whitespace characters http://www.w3.org/TR/css3-selectors/#whitespace
+	whitespace = "[\\x20\\t\\r\\n\\f]",
+	// http://www.w3.org/TR/css3-syntax/#characters
+	characterEncoding = "(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",
+
+	// Loosely modeled on CSS identifier characters
+	// An unquoted value should be a CSS identifier http://www.w3.org/TR/css3-selectors/#attribute-selectors
+	// Proper syntax: http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier
+	identifier = characterEncoding.replace( "w", "w#" ),
+
+	// Attribute selectors: http://www.w3.org/TR/selectors/#attribute-selectors
+	attributes = "\\[" + whitespace + "*(" + characterEncoding + ")(?:" + whitespace +
+		// Operator (capture 2)
+		"*([*^$|!~]?=)" + whitespace +
+		// "Attribute values must be CSS identifiers [capture 5] or strings [capture 3 or capture 4]"
+		"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" + whitespace +
+		"*\\]",
+
+	pseudos = ":(" + characterEncoding + ")(?:\\((" +
+		// To reduce the number of selectors needing tokenize in the preFilter, prefer arguments:
+		// 1. quoted (capture 3; capture 4 or capture 5)
+		"('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|" +
+		// 2. simple (capture 6)
+		"((?:\\\\.|[^\\\\()[\\]]|" + attributes + ")*)|" +
+		// 3. anything else (capture 2)
+		".*" +
+		")\\)|)",
+
+	// Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter
+	rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ),
+
+	rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ),
+	rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*" ),
+
+	rattributeQuotes = new RegExp( "=" + whitespace + "*([^\\]'\"]*?)" + whitespace + "*\\]", "g" ),
+
+	rpseudo = new RegExp( pseudos ),
+	ridentifier = new RegExp( "^" + identifier + "$" ),
+
+	matchExpr = {
+		"ID": new RegExp( "^#(" + characterEncoding + ")" ),
+		"CLASS": new RegExp( "^\\.(" + characterEncoding + ")" ),
+		"TAG": new RegExp( "^(" + characterEncoding.replace( "w", "w*" ) + ")" ),
+		"ATTR": new RegExp( "^" + attributes ),
+		"PSEUDO": new RegExp( "^" + pseudos ),
+		"CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace +
+			"*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace +
+			"*(\\d+)|))" + whitespace + "*\\)|)", "i" ),
+		"bool": new RegExp( "^(?:" + booleans + ")$", "i" ),
+		// For use in libraries implementing .is()
+		// We use this for POS matching in `select`
+		"needsContext": new RegExp( "^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" +
+			whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" )
+	},
+
+	rinputs = /^(?:input|select|textarea|button)$/i,
+	rheader = /^h\d$/i,
+
+	rnative = /^[^{]+\{\s*\[native \w/,
+
+	// Easily-parseable/retrievable ID or TAG or CLASS selectors
+	rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,
+
+	rsibling = /[+~]/,
+	rescape = /'|\\/g,
+
+	// CSS escapes http://www.w3.org/TR/CSS21/syndata.html#escaped-characters
+	runescape = new RegExp( "\\\\([\\da-f]{1,6}" + whitespace + "?|(" + whitespace + ")|.)", "ig" ),
+	funescape = function( _, escaped, escapedWhitespace ) {
+		var high = "0x" + escaped - 0x10000;
+		// NaN means non-codepoint
+		// Support: Firefox<24
+		// Workaround erroneous numeric interpretation of +"0x"
+		return high !== high || escapedWhitespace ?
+			escaped :
+			high < 0 ?
+				// BMP codepoint
+				String.fromCharCode( high + 0x10000 ) :
+				// Supplemental Plane codepoint (surrogate pair)
+				String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 );
+	};
+
+// Optimize for push.apply( _, NodeList )
+try {
+	push.apply(
+		(arr = slice.call( preferredDoc.childNodes )),
+		preferredDoc.childNodes
+	);
+	// Support: Android<4.0
+	// Detect silently failing push.apply
+	arr[ preferredDoc.childNodes.length ].nodeType;
+} catch ( e ) {
+	push = { apply: arr.length ?
+
+		// Leverage slice if possible
+		function( target, els ) {
+			push_native.apply( target, slice.call(els) );
+		} :
+
+		// Support: IE<9
+		// Otherwise append directly
+		function( target, els ) {
+			var j = target.length,
+				i = 0;
+			// Can't trust NodeList.length
+			while ( (target[j++] = els[i++]) ) {}
+			target.length = j - 1;
+		}
+	};
+}
+
+function Sizzle( selector, context, results, seed ) {
+	var match, elem, m, nodeType,
+		// QSA vars
+		i, groups, old, nid, newContext, newSelector;
+
+	if ( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) {
+		setDocument( context );
+	}
+
+	context = context || document;
+	results = results || [];
+
+	if ( !selector || typeof selector !== "string" ) {
+		return results;
+	}
+
+	if ( (nodeType = context.nodeType) !== 1 && nodeType !== 9 ) {
+		return [];
+	}
+
+	if ( documentIsHTML && !seed ) {
+
+		// Shortcuts
+		if ( (match = rquickExpr.exec( selector )) ) {
+			// Speed-up: Sizzle("#ID")
+			if ( (m = match[1]) ) {
+				if ( nodeType === 9 ) {
+					elem = context.getElementById( m );
+					// Check parentNode to catch when Blackberry 4.6 returns
+					// nodes that are no longer in the document (jQuery #6963)
+					if ( elem && elem.parentNode ) {
+						// Handle the case where IE, Opera, and Webkit return items
+						// by name instead of ID
+						if ( elem.id === m ) {
+							results.push( elem );
+							return results;
+						}
+					} else {
+						return results;
+					}
+				} else {
+					// Context is not a document
+					if ( context.ownerDocument && (elem = context.ownerDocument.getElementById( m )) &&
+						contains( context, elem ) && elem.id === m ) {
+						results.push( elem );
+						return results;
+					}
+				}
+
+			// Speed-up: Sizzle("TAG")
+			} else if ( match[2] ) {
+				push.apply( results, context.getElementsByTagName( selector ) );
+				return results;
+
+			// Speed-up: Sizzle(".CLASS")
+			} else if ( (m = match[3]) && support.getElementsByClassName && context.getElementsByClassName ) {
+				push.apply( results, context.getElementsByClassName( m ) );
+				return results;
+			}
+		}
+
+		// QSA path
+		if ( support.qsa && (!rbuggyQSA || !rbuggyQSA.test( selector )) ) {
+			nid = old = expando;
+			newContext = context;
+			newSelector = nodeType === 9 && selector;
+
+			// qSA works strangely on Element-rooted queries
+			// We can work around this by specifying an extra ID on the root
+			// and working up from there (Thanks to Andrew Dupont for the technique)
+			// IE 8 doesn't work on object elements
+			if ( nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) {
+				groups = tokenize( selector );
+
+				if ( (old = context.getAttribute("id")) ) {
+					nid = old.replace( rescape, "\\$&" );
+				} else {
+					context.setAttribute( "id", nid );
+				}
+				nid = "[id='" + nid + "'] ";
+
+				i = groups.length;
+				while ( i-- ) {
+					groups[i] = nid + toSelector( groups[i] );
+				}
+				newContext = rsibling.test( selector ) && testContext( context.parentNode ) || context;
+				newSelector = groups.join(",");
+			}
+
+			if ( newSelector ) {
+				try {
+					push.apply( results,
+						newContext.querySelectorAll( newSelector )
+					);
+					return results;
+				} catch(qsaError) {
+				} finally {
+					if ( !old ) {
+						context.removeAttribute("id");
+					}
+				}
+			}
+		}
+	}
+
+	// All others
+	return select( selector.replace( rtrim, "$1" ), context, results, seed );
+}
+
+/**
+ * Create key-value caches of limited size
+ * @returns {Function(string, Object)} Returns the Object data after storing it on itself with
+ *	property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength)
+ *	deleting the oldest entry
+ */
+function createCache() {
+	var keys = [];
+
+	function cache( key, value ) {
+		// Use (key + " ") to avoid collision with native prototype properties (see Issue #157)
+		if ( keys.push( key + " " ) > Expr.cacheLength ) {
+			// Only keep the most recent entries
+			delete cache[ keys.shift() ];
+		}
+		return (cache[ key + " " ] = value);
+	}
+	return cache;
+}
+
+/**
+ * Mark a function for special use by Sizzle
+ * @param {Function} fn The function to mark
+ */
+function markFunction( fn ) {
+	fn[ expando ] = true;
+	return fn;
+}
+
+/**
+ * Support testing using an element
+ * @param {Function} fn Passed the created div and expects a boolean result
+ */
+function assert( fn ) {
+	var div = document.createElement("div");
+
+	try {
+		return !!fn( div );
+	} catch (e) {
+		return false;
+	} finally {
+		// Remove from its parent by default
+		if ( div.parentNode ) {
+			div.parentNode.removeChild( div );
+		}
+		// release memory in IE
+		div = null;
+	}
+}
+
+/**
+ * Adds the same handler for all of the specified attrs
+ * @param {String} attrs Pipe-separated list of attributes
+ * @param {Function} handler The method that will be applied
+ */
+function addHandle( attrs, handler ) {
+	var arr = attrs.split("|"),
+		i = attrs.length;
+
+	while ( i-- ) {
+		Expr.attrHandle[ arr[i] ] = handler;
+	}
+}
+
+/**
+ * Checks document order of two siblings
+ * @param {Element} a
+ * @param {Element} b
+ * @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b
+ */
+function siblingCheck( a, b ) {
+	var cur = b && a,
+		diff = cur && a.nodeType === 1 && b.nodeType === 1 &&
+			( ~b.sourceIndex || MAX_NEGATIVE ) -
+			( ~a.sourceIndex || MAX_NEGATIVE );
+
+	// Use IE sourceIndex if available on both nodes
+	if ( diff ) {
+		return diff;
+	}
+
+	// Check if b follows a
+	if ( cur ) {
+		while ( (cur = cur.nextSibling) ) {
+			if ( cur === b ) {
+				return -1;
+			}
+		}
+	}
+
+	return a ? 1 : -1;
+}
+
+/**
+ * Returns a function to use in pseudos for input types
+ * @param {String} type
+ */
+function createInputPseudo( type ) {
+	return function( elem ) {
+		var name = elem.nodeName.toLowerCase();
+		return name === "input" && elem.type === type;
+	};
+}
+
+/**
+ * Returns a function to use in pseudos for buttons
+ * @param {String} type
+ */
+function createButtonPseudo( type ) {
+	return function( elem ) {
+		var name = elem.nodeName.toLowerCase();
+		return (name === "input" || name === "button") && elem.type === type;
+	};
+}
+
+/**
+ * Returns a function to use in pseudos for positionals
+ * @param {Function} fn
+ */
+function createPositionalPseudo( fn ) {
+	return markFunction(function( argument ) {
+		argument = +argument;
+		return markFunction(function( seed, matches ) {
+			var j,
+				matchIndexes = fn( [], seed.length, argument ),
+				i = matchIndexes.length;
+
+			// Match elements found at the specified indexes
+			while ( i-- ) {
+				if ( seed[ (j = matchIndexes[i]) ] ) {
+					seed[j] = !(matches[j] = seed[j]);
+				}
+			}
+		});
+	});
+}
+
+/**
+ * Checks a node for validity as a Sizzle context
+ * @param {Element|Object=} context
+ * @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value
+ */
+function testContext( context ) {
+	return context && typeof context.getElementsByTagName !== strundefined && context;
+}
+
+// Expose support vars for convenience
+support = Sizzle.support = {};
+
+/**
+ * Detects XML nodes
+ * @param {Element|Object} elem An element or a document
+ * @returns {Boolean} True iff elem is a non-HTML XML node
+ */
+isXML = Sizzle.isXML = function( elem ) {
+	// documentElement is verified for cases where it doesn't yet exist
+	// (such as loading iframes in IE - #4833)
+	var documentElement = elem && (elem.ownerDocument || elem).documentElement;
+	return documentElement ? documentElement.nodeName !== "HTML" : false;
+};
+
+/**
+ * Sets document-related variables once based on the current document
+ * @param {Element|Object} [doc] An element or document object to use to set the document
+ * @returns {Object} Returns the current document
+ */
+setDocument = Sizzle.setDocument = function( node ) {
+	var hasCompare,
+		doc = node ? node.ownerDocument || node : preferredDoc,
+		parent = doc.defaultView;
+
+	// If no document and documentElement is available, return
+	if ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) {
+		return document;
+	}
+
+	// Set our document
+	document = doc;
+	docElem = doc.documentElement;
+
+	// Support tests
+	documentIsHTML = !isXML( doc );
+
+	// Support: IE>8
+	// If iframe document is assigned to "document" variable and if iframe has been reloaded,
+	// IE will throw "permission denied" error when accessing "document" variable, see jQuery #13936
+	// IE6-8 do not support the defaultView property so parent will be undefined
+	if ( parent && parent !== parent.top ) {
+		// IE11 does not have attachEvent, so all must suffer
+		if ( parent.addEventListener ) {
+			parent.addEventListener( "unload", function() {
+				setDocument();
+			}, false );
+		} else if ( parent.attachEvent ) {
+			parent.attachEvent( "onunload", function() {
+				setDocument();
+			});
+		}
+	}
+
+	/* Attributes
+	---------------------------------------------------------------------- */
+
+	// Support: IE<8
+	// Verify that getAttribute really returns attributes and not properties (excepting IE8 booleans)
+	support.attributes = assert(function( div ) {
+		div.className = "i";
+		return !div.getAttribute("className");
+	});
+
+	/* getElement(s)By*
+	---------------------------------------------------------------------- */
+
+	// Check if getElementsByTagName("*") returns only elements
+	support.getElementsByTagName = assert(function( div ) {
+		div.appendChild( doc.createComment("") );
+		return !div.getElementsByTagName("*").length;
+	});
+
+	// Check if getElementsByClassName can be trusted
+	support.getElementsByClassName = rnative.test( doc.getElementsByClassName ) && assert(function( div ) {
+		div.innerHTML = "<div class='a'></div><div class='a i'></div>";
+
+		// Support: Safari<4
+		// Catch class over-caching
+		div.firstChild.className = "i";
+		// Support: Opera<10
+		// Catch gEBCN failure to find non-leading classes
+		return div.getElementsByClassName("i").length === 2;
+	});
+
+	// Support: IE<10
+	// Check if getElementById returns elements by name
+	// The broken getElementById methods don't pick up programatically-set names,
+	// so use a roundabout getElementsByName test
+	support.getById = assert(function( div ) {
+		docElem.appendChild( div ).id = expando;
+		return !doc.getElementsByName || !doc.getElementsByName( expando ).length;
+	});
+
+	// ID find and filter
+	if ( support.getById ) {
+		Expr.find["ID"] = function( id, context ) {
+			if ( typeof context.getElementById !== strundefined && documentIsHTML ) {
+				var m = context.getElementById( id );
+				// Check parentNode to catch when Blackberry 4.6 returns
+				// nodes that are no longer in the document #6963
+				return m && m.parentNode ? [ m ] : [];
+			}
+		};
+		Expr.filter["ID"] = function( id ) {
+			var attrId = id.replace( runescape, funescape );
+			return function( elem ) {
+				return elem.getAttribute("id") === attrId;
+			};
+		};
+	} else {
+		// Support: IE6/7
+		// getElementById is not reliable as a find shortcut
+		delete Expr.find["ID"];
+
+		Expr.filter["ID"] =  function( id ) {
+			var attrId = id.replace( runescape, funescape );
+			return function( elem ) {
+				var node = typeof elem.getAttributeNode !== strundefined && elem.getAttributeNode("id");
+				return node && node.value === attrId;
+			};
+		};
+	}
+
+	// Tag
+	Expr.find["TAG"] = support.getElementsByTagName ?
+		function( tag, context ) {
+			if ( typeof context.getElementsByTagName !== strundefined ) {
+				return context.getElementsByTagName( tag );
+			}
+		} :
+		function( tag, context ) {
+			var elem,
+				tmp = [],
+				i = 0,
+				results = context.getElementsByTagName( tag );
+
+			// Filter out possible comments
+			if ( tag === "*" ) {
+				while ( (elem = results[i++]) ) {
+					if ( elem.nodeType === 1 ) {
+						tmp.push( elem );
+					}
+				}
+
+				return tmp;
+			}
+			return results;
+		};
+
+	// Class
+	Expr.find["CLASS"] = support.getElementsByClassName && function( className, context ) {
+		if ( typeof context.getElementsByClassName !== strundefined && documentIsHTML ) {
+			return context.getElementsByClassName( className );
+		}
+	};
+
+	/* QSA/matchesSelector
+	---------------------------------------------------------------------- */
+
+	// QSA and matchesSelector support
+
+	// matchesSelector(:active) reports false when true (IE9/Opera 11.5)
+	rbuggyMatches = [];
+
+	// qSa(:focus) reports false when true (Chrome 21)
+	// We allow this because of a bug in IE8/9 that throws an error
+	// whenever `document.activeElement` is accessed on an iframe
+	// So, we allow :focus to pass through QSA all the time to avoid the IE error
+	// See http://bugs.jquery.com/ticket/13378
+	rbuggyQSA = [];
+
+	if ( (support.qsa = rnative.test( doc.querySelectorAll )) ) {
+		// Build QSA regex
+		// Regex strategy adopted from Diego Perini
+		assert(function( div ) {
+			// Select is set to empty string on purpose
+			// This is to test IE's treatment of not explicitly
+			// setting a boolean content attribute,
+			// since its presence should be enough
+			// http://bugs.jquery.com/ticket/12359
+			div.innerHTML = "<select msallowclip=''><option selected=''></option></select>";
+
+			// Support: IE8, Opera 11-12.16
+			// Nothing should be selected when empty strings follow ^= or $= or *=
+			// The test attribute must be unknown in Opera but "safe" for WinRT
+			// http://msdn.microsoft.com/en-us/library/ie/hh465388.aspx#attribute_section
+			if ( div.querySelectorAll("[msallowclip^='']").length ) {
+				rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" );
+			}
+
+			// Support: IE8
+			// Boolean attributes and "value" are not treated correctly
+			if ( !div.querySelectorAll("[selected]").length ) {
+				rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" );
+			}
+
+			// Webkit/Opera - :checked should return selected option elements
+			// http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
+			// IE8 throws error here and will not see later tests
+			if ( !div.querySelectorAll(":checked").length ) {
+				rbuggyQSA.push(":checked");
+			}
+		});
+
+		assert(function( div ) {
+			// Support: Windows 8 Native Apps
+			// The type and name attributes are restricted during .innerHTML assignment
+			var input = doc.createElement("input");
+			input.setAttribute( "type", "hidden" );
+			div.appendChild( input ).setAttribute( "name", "D" );
+
+			// Support: IE8
+			// Enforce case-sensitivity of name attribute
+			if ( div.querySelectorAll("[name=d]").length ) {
+				rbuggyQSA.push( "name" + whitespace + "*[*^$|!~]?=" );
+			}
+
+			// FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled)
+			// IE8 throws error here and will not see later tests
+			if ( !div.querySelectorAll(":enabled").length ) {
+				rbuggyQSA.push( ":enabled", ":disabled" );
+			}
+
+			// Opera 10-11 does not throw on post-comma invalid pseudos
+			div.querySelectorAll("*,:x");
+			rbuggyQSA.push(",.*:");
+		});
+	}
+
+	if ( (support.matchesSelector = rnative.test( (matches = docElem.matches ||
+		docElem.webkitMatchesSelector ||
+		docElem.mozMatchesSelector ||
+		docElem.oMatchesSelector ||
+		docElem.msMatchesSelector) )) ) {
+
+		assert(function( div ) {
+			// Check to see if it's possible to do matchesSelector
+			// on a disconnected node (IE 9)
+			support.disconnectedMatch = matches.call( div, "div" );
+
+			// This should fail with an exception
+			// Gecko does not error, returns false instead
+			matches.call( div, "[s!='']:x" );
+			rbuggyMatches.push( "!=", pseudos );
+		});
+	}
+
+	rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join("|") );
+	rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join("|") );
+
+	/* Contains
+	---------------------------------------------------------------------- */
+	hasCompare = rnative.test( docElem.compareDocumentPosition );
+
+	// Element contains another
+	// Purposefully does not implement inclusive descendent
+	// As in, an element does not contain itself
+	contains = hasCompare || rnative.test( docElem.contains ) ?
+		function( a, b ) {
+			var adown = a.nodeType === 9 ? a.documentElement : a,
+				bup = b && b.parentNode;
+			return a === bup || !!( bup && bup.nodeType === 1 && (
+				adown.contains ?
+					adown.contains( bup ) :
+					a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16
+			));
+		} :
+		function( a, b ) {
+			if ( b ) {
+				while ( (b = b.parentNode) ) {
+					if ( b === a ) {
+						return true;
+					}
+				}
+			}
+			return false;
+		};
+
+	/* Sorting
+	---------------------------------------------------------------------- */
+
+	// Document order sorting
+	sortOrder = hasCompare ?
+	function( a, b ) {
+
+		// Flag for duplicate removal
+		if ( a === b ) {
+			hasDuplicate = true;
+			return 0;
+		}
+
+		// Sort on method existence if only one input has compareDocumentPosition
+		var compare = !a.compareDocumentPosition - !b.compareDocumentPosition;
+		if ( compare ) {
+			return compare;
+		}
+
+		// Calculate position if both inputs belong to the same document
+		compare = ( a.ownerDocument || a ) === ( b.ownerDocument || b ) ?
+			a.compareDocumentPosition( b ) :
+
+			// Otherwise we know they are disconnected
+			1;
+
+		// Disconnected nodes
+		if ( compare & 1 ||
+			(!support.sortDetached && b.compareDocumentPosition( a ) === compare) ) {
+
+			// Choose the first element that is related to our preferred document
+			if ( a === doc || a.ownerDocument === preferredDoc && contains(preferredDoc, a) ) {
+				return -1;
+			}
+			if ( b === doc || b.ownerDocument === preferredDoc && contains(preferredDoc, b) ) {
+				return 1;
+			}
+
+			// Maintain original order
+			return sortInput ?
+				( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) :
+				0;
+		}
+
+		return compare & 4 ? -1 : 1;
+	} :
+	function( a, b ) {
+		// Exit early if the nodes are identical
+		if ( a === b ) {
+			hasDuplicate = true;
+			return 0;
+		}
+
+		var cur,
+			i = 0,
+			aup = a.parentNode,
+			bup = b.parentNode,
+			ap = [ a ],
+			bp = [ b ];
+
+		// Parentless nodes are either documents or disconnected
+		if ( !aup || !bup ) {
+			return a === doc ? -1 :
+				b === doc ? 1 :
+				aup ? -1 :
+				bup ? 1 :
+				sortInput ?
+				( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) :
+				0;
+
+		// If the nodes are siblings, we can do a quick check
+		} else if ( aup === bup ) {
+			return siblingCheck( a, b );
+		}
+
+		// Otherwise we need full lists of their ancestors for comparison
+		cur = a;
+		while ( (cur = cur.parentNode) ) {
+			ap.unshift( cur );
+		}
+		cur = b;
+		while ( (cur = cur.parentNode) ) {
+			bp.unshift( cur );
+		}
+
+		// Walk down the tree looking for a discrepancy
+		while ( ap[i] === bp[i] ) {
+			i++;
+		}
+
+		return i ?
+			// Do a sibling check if the nodes have a common ancestor
+			siblingCheck( ap[i], bp[i] ) :
+
+			// Otherwise nodes in our document sort first
+			ap[i] === preferredDoc ? -1 :
+			bp[i] === preferredDoc ? 1 :
+			0;
+	};
+
+	return doc;
+};
+
+Sizzle.matches = function( expr, elements ) {
+	return Sizzle( expr, null, null, elements );
+};
+
+Sizzle.matchesSelector = function( elem, expr ) {
+	// Set document vars if needed
+	if ( ( elem.ownerDocument || elem ) !== document ) {
+		setDocument( elem );
+	}
+
+	// Make sure that attribute selectors are quoted
+	expr = expr.replace( rattributeQuotes, "='$1']" );
+
+	if ( support.matchesSelector && documentIsHTML &&
+		( !rbuggyMatches || !rbuggyMatches.test( expr ) ) &&
+		( !rbuggyQSA     || !rbuggyQSA.test( expr ) ) ) {
+
+		try {
+			var ret = matches.call( elem, expr );
+
+			// IE 9's matchesSelector returns false on disconnected nodes
+			if ( ret || support.disconnectedMatch ||
+					// As well, disconnected nodes are said to be in a document
+					// fragment in IE 9
+					elem.document && elem.document.nodeType !== 11 ) {
+				return ret;
+			}
+		} catch(e) {}
+	}
+
+	return Sizzle( expr, document, null, [ elem ] ).length > 0;
+};
+
+Sizzle.contains = function( context, elem ) {
+	// Set document vars if needed
+	if ( ( context.ownerDocument || context ) !== document ) {
+		setDocument( context );
+	}
+	return contains( context, elem );
+};
+
+Sizzle.attr = function( elem, name ) {
+	// Set document vars if needed
+	if ( ( elem.ownerDocument || elem ) !== document ) {
+		setDocument( elem );
+	}
+
+	var fn = Expr.attrHandle[ name.toLowerCase() ],
+		// Don't get fooled by Object.prototype properties (jQuery #13807)
+		val = fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ?
+			fn( elem, name, !documentIsHTML ) :
+			undefined;
+
+	return val !== undefined ?
+		val :
+		support.attributes || !documentIsHTML ?
+			elem.getAttribute( name ) :
+			(val = elem.getAttributeNode(name)) && val.specified ?
+				val.value :
+				null;
+};
+
+Sizzle.error = function( msg ) {
+	throw new Error( "Syntax error, unrecognized expression: " + msg );
+};
+
+/**
+ * Document sorting and removing duplicates
+ * @param {ArrayLike} results
+ */
+Sizzle.uniqueSort = function( results ) {
+	var elem,
+		duplicates = [],
+		j = 0,
+		i = 0;
+
+	// Unless we *know* we can detect duplicates, assume their presence
+	hasDuplicate = !support.detectDuplicates;
+	sortInput = !support.sortStable && results.slice( 0 );
+	results.sort( sortOrder );
+
+	if ( hasDuplicate ) {
+		while ( (elem = results[i++]) ) {
+			if ( elem === results[ i ] ) {
+				j = duplicates.push( i );
+			}
+		}
+		while ( j-- ) {
+			results.splice( duplicates[ j ], 1 );
+		}
+	}
+
+	// Clear input after sorting to release objects
+	// See https://github.com/jquery/sizzle/pull/225
+	sortInput = null;
+
+	return results;
+};
+
+/**
+ * Utility function for retrieving the text value of an array of DOM nodes
+ * @param {Array|Element} elem
+ */
+getText = Sizzle.getText = function( elem ) {
+	var node,
+		ret = "",
+		i = 0,
+		nodeType = elem.nodeType;
+
+	if ( !nodeType ) {
+		// If no nodeType, this is expected to be an array
+		while ( (node = elem[i++]) ) {
+			// Do not traverse comment nodes
+			ret += getText( node );
+		}
+	} else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) {
+		// Use textContent for elements
+		// innerText usage removed for consistency of new lines (jQuery #11153)
+		if ( typeof elem.textContent === "string" ) {
+			return elem.textContent;
+		} else {
+			// Traverse its children
+			for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {
+				ret += getText( elem );
+			}
+		}
+	} else if ( nodeType === 3 || nodeType === 4 ) {
+		return elem.nodeValue;
+	}
+	// Do not include comment or processing instruction nodes
+
+	return ret;
+};
+
+Expr = Sizzle.selectors = {
+
+	// Can be adjusted by the user
+	cacheLength: 50,
+
+	createPseudo: markFunction,
+
+	match: matchExpr,
+
+	attrHandle: {},
+
+	find: {},
+
+	relative: {
+		">": { dir: "parentNode", first: true },
+		" ": { dir: "parentNode" },
+		"+": { dir: "previousSibling", first: true },
+		"~": { dir: "previousSibling" }
+	},
+
+	preFilter: {
+		"ATTR": function( match ) {
+			match[1] = match[1].replace( runescape, funescape );
+
+			// Move the given value to match[3] whether quoted or unquoted
+			match[3] = ( match[3] || match[4] || match[5] || "" ).replace( runescape, funescape );
+
+			if ( match[2] === "~=" ) {
+				match[3] = " " + match[3] + " ";
+			}
+
+			return match.slice( 0, 4 );
+		},
+
+		"CHILD": function( match ) {
+			/* matches from matchExpr["CHILD"]
+				1 type (only|nth|...)
+				2 what (child|of-type)
+				3 argument (even|odd|\d*|\d*n([+-]\d+)?|...)
+				4 xn-component of xn+y argument ([+-]?\d*n|)
+				5 sign of xn-component
+				6 x of xn-component
+				7 sign of y-component
+				8 y of y-component
+			*/
+			match[1] = match[1].toLowerCase();
+
+			if ( match[1].slice( 0, 3 ) === "nth" ) {
+				// nth-* requires argument
+				if ( !match[3] ) {
+					Sizzle.error( match[0] );
+				}
+
+				// numeric x and y parameters for Expr.filter.CHILD
+				// remember that false/true cast respectively to 0/1
+				match[4] = +( match[4] ? match[5] + (match[6] || 1) : 2 * ( match[3] === "even" || match[3] === "odd" ) );
+				match[5] = +( ( match[7] + match[8] ) || match[3] === "odd" );
+
+			// other types prohibit arguments
+			} else if ( match[3] ) {
+				Sizzle.error( match[0] );
+			}
+
+			return match;
+		},
+
+		"PSEUDO": function( match ) {
+			var excess,
+				unquoted = !match[6] && match[2];
+
+			if ( matchExpr["CHILD"].test( match[0] ) ) {
+				return null;
+			}
+
+			// Accept quoted arguments as-is
+			if ( match[3] ) {
+				match[2] = match[4] || match[5] || "";
+
+			// Strip excess characters from unquoted arguments
+			} else if ( unquoted && rpseudo.test( unquoted ) &&
+				// Get excess from tokenize (recursively)
+				(excess = tokenize( unquoted, true )) &&
+				// advance to the next closing parenthesis
+				(excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length) ) {
+
+				// excess is a negative index
+				match[0] = match[0].slice( 0, excess );
+				match[2] = unquoted.slice( 0, excess );
+			}
+
+			// Return only captures needed by the pseudo filter method (type and argument)
+			return match.slice( 0, 3 );
+		}
+	},
+
+	filter: {
+
+		"TAG": function( nodeNameSelector ) {
+			var nodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase();
+			return nodeNameSelector === "*" ?
+				function() { return true; } :
+				function( elem ) {
+					return elem.nodeName && elem.nodeName.toLowerCase() === nodeName;
+				};
+		},
+
+		"CLASS": function( className ) {
+			var pattern = classCache[ className + " " ];
+
+			return pattern ||
+				(pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) &&
+				classCache( className, function( elem ) {
+					return pattern.test( typeof elem.className === "string" && elem.className || typeof elem.getAttribute !== strundefined && elem.getAttribute("class") || "" );
+				});
+		},
+
+		"ATTR": function( name, operator, check ) {
+			return function( elem ) {
+				var result = Sizzle.attr( elem, name );
+
+				if ( result == null ) {
+					return operator === "!=";
+				}
+				if ( !operator ) {
+					return true;
+				}
+
+				result += "";
+
+				return operator === "=" ? result === check :
+					operator === "!=" ? result !== check :
+					operator === "^=" ? check && result.indexOf( check ) === 0 :
+					operator === "*=" ? check && result.indexOf( check ) > -1 :
+					operator === "$=" ? check && result.slice( -check.length ) === check :
+					operator === "~=" ? ( " " + result + " " ).indexOf( check ) > -1 :
+					operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" :
+					false;
+			};
+		},
+
+		"CHILD": function( type, what, argument, first, last ) {
+			var simple = type.slice( 0, 3 ) !== "nth",
+				forward = type.slice( -4 ) !== "last",
+				ofType = what === "of-type";
+
+			return first === 1 && last === 0 ?
+
+				// Shortcut for :nth-*(n)
+				function( elem ) {
+					return !!elem.parentNode;
+				} :
+
+				function( elem, context, xml ) {
+					var cache, outerCache, node, diff, nodeIndex, start,
+						dir = simple !== forward ? "nextSibling" : "previousSibling",
+						parent = elem.parentNode,
+						name = ofType && elem.nodeName.toLowerCase(),
+						useCache = !xml && !ofType;
+
+					if ( parent ) {
+
+						// :(first|last|only)-(child|of-type)
+						if ( simple ) {
+							while ( dir ) {
+								node = elem;
+								while ( (node = node[ dir ]) ) {
+									if ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) {
+										return false;
+									}
+								}
+								// Reverse direction for :only-* (if we haven't yet done so)
+								start = dir = type === "only" && !start && "nextSibling";
+							}
+							return true;
+						}
+
+						start = [ forward ? parent.firstChild : parent.lastChild ];
+
+						// non-xml :nth-child(...) stores cache data on `parent`
+						if ( forward && useCache ) {
+							// Seek `elem` from a previously-cached index
+							outerCache = parent[ expando ] || (parent[ expando ] = {});
+							cache = outerCache[ type ] || [];
+							nodeIndex = cache[0] === dirruns && cache[1];
+							diff = cache[0] === dirruns && cache[2];
+							node = nodeIndex && parent.childNodes[ nodeIndex ];
+
+							while ( (node = ++nodeIndex && node && node[ dir ] ||
+
+								// Fallback to seeking `elem` from the start
+								(diff = nodeIndex = 0) || start.pop()) ) {
+
+								// When found, cache indexes on `parent` and break
+								if ( node.nodeType === 1 && ++diff && node === elem ) {
+									outerCache[ type ] = [ dirruns, nodeIndex, diff ];
+									break;
+								}
+							}
+
+						// Use previously-cached element index if available
+						} else if ( useCache && (cache = (elem[ expando ] || (elem[ expando ] = {}))[ type ]) && cache[0] === dirruns ) {
+							diff = cache[1];
+
+						// xml :nth-child(...) or :nth-last-child(...) or :nth(-last)?-of-type(...)
+						} else {
+							// Use the same loop as above to seek `elem` from the start
+							while ( (node = ++nodeIndex && node && node[ dir ] ||
+								(diff = nodeIndex = 0) || start.pop()) ) {
+
+								if ( ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) && ++diff ) {
+									// Cache the index of each encountered element
+									if ( useCache ) {
+										(node[ expando ] || (node[ expando ] = {}))[ type ] = [ dirruns, diff ];
+									}
+
+									if ( node === elem ) {
+										break;
+									}
+								}
+							}
+						}
+
+						// Incorporate the offset, then check against cycle size
+						diff -= last;
+						return diff === first || ( diff % first === 0 && diff / first >= 0 );
+					}
+				};
+		},
+
+		"PSEUDO": function( pseudo, argument ) {
+			// pseudo-class names are case-insensitive
+			// http://www.w3.org/TR/selectors/#pseudo-classes
+			// Prioritize by case sensitivity in case custom pseudos are added with uppercase letters
+			// Remember that setFilters inherits from pseudos
+			var args,
+				fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] ||
+					Sizzle.error( "unsupported pseudo: " + pseudo );
+
+			// The user may use createPseudo to indicate that
+			// arguments are needed to create the filter function
+			// just as Sizzle does
+			if ( fn[ expando ] ) {
+				return fn( argument );
+			}
+
+			// But maintain support for old signatures
+			if ( fn.length > 1 ) {
+				args = [ pseudo, pseudo, "", argument ];
+				return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ?
+					markFunction(function( seed, matches ) {
+						var idx,
+							matched = fn( seed, argument ),
+							i = matched.length;
+						while ( i-- ) {
+							idx = indexOf.call( seed, matched[i] );
+							seed[ idx ] = !( matches[ idx ] = matched[i] );
+						}
+					}) :
+					function( elem ) {
+						return fn( elem, 0, args );
+					};
+			}
+
+			return fn;
+		}
+	},
+
+	pseudos: {
+		// Potentially complex pseudos
+		"not": markFunction(function( selector ) {
+			// Trim the selector passed to compile
+			// to avoid treating leading and trailing
+			// spaces as combinators
+			var input = [],
+				results = [],
+				matcher = compile( selector.replace( rtrim, "$1" ) );
+
+			return matcher[ expando ] ?
+				markFunction(function( seed, matches, context, xml ) {
+					var elem,
+						unmatched = matcher( seed, null, xml, [] ),
+						i = seed.length;
+
+					// Match elements unmatched by `matcher`
+					while ( i-- ) {
+						if ( (elem = unmatched[i]) ) {
+							seed[i] = !(matches[i] = elem);
+						}
+					}
+				}) :
+				function( elem, context, xml ) {
+					input[0] = elem;
+					matcher( input, null, xml, results );
+					return !results.pop();
+				};
+		}),
+
+		"has": markFunction(function( selector ) {
+			return function( elem ) {
+				return Sizzle( selector, elem ).length > 0;
+			};
+		}),
+
+		"contains": markFunction(function( text ) {
+			return function( elem ) {
+				return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1;
+			};
+		}),
+
+		// "Whether an element is represented by a :lang() selector
+		// is based solely on the element's language value
+		// being equal to the identifier C,
+		// or beginning with the identifier C immediately followed by "-".
+		// The matching of C against the element's language value is performed case-insensitively.
+		// The identifier C does not have to be a valid language name."
+		// http://www.w3.org/TR/selectors/#lang-pseudo
+		"lang": markFunction( function( lang ) {
+			// lang value must be a valid identifier
+			if ( !ridentifier.test(lang || "") ) {
+				Sizzle.error( "unsupported lang: " + lang );
+			}
+			lang = lang.replace( runescape, funescape ).toLowerCase();
+			return function( elem ) {
+				var elemLang;
+				do {
+					if ( (elemLang = documentIsHTML ?
+						elem.lang :
+						elem.getAttribute("xml:lang") || elem.getAttribute("lang")) ) {
+
+						elemLang = elemLang.toLowerCase();
+						return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0;
+					}
+				} while ( (elem = elem.parentNode) && elem.nodeType === 1 );
+				return false;
+			};
+		}),
+
+		// Miscellaneous
+		"target": function( elem ) {
+			var hash = window.location && window.location.hash;
+			return hash && hash.slice( 1 ) === elem.id;
+		},
+
+		"root": function( elem ) {
+			return elem === docElem;
+		},
+
+		"focus": function( elem ) {
+			return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex);
+		},
+
+		// Boolean properties
+		"enabled": function( elem ) {
+			return elem.disabled === false;
+		},
+
+		"disabled": function( elem ) {
+			return elem.disabled === true;
+		},
+
+		"checked": function( elem ) {
+			// In CSS3, :checked should return both checked and selected elements
+			// http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
+			var nodeName = elem.nodeName.toLowerCase();
+			return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected);
+		},
+
+		"selected": function( elem ) {
+			// Accessing this property makes selected-by-default
+			// options in Safari work properly
+			if ( elem.parentNode ) {
+				elem.parentNode.selectedIndex;
+			}
+
+			return elem.selected === true;
+		},
+
+		// Contents
+		"empty": function( elem ) {
+			// http://www.w3.org/TR/selectors/#empty-pseudo
+			// :empty is negated by element (1) or content nodes (text: 3; cdata: 4; entity ref: 5),
+			//   but not by others (comment: 8; processing instruction: 7; etc.)
+			// nodeType < 6 works because attributes (2) do not appear as children
+			for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {
+				if ( elem.nodeType < 6 ) {
+					return false;
+				}
+			}
+			return true;
+		},
+
+		"parent": function( elem ) {
+			return !Expr.pseudos["empty"]( elem );
+		},
+
+		// Element/input types
+		"header": function( elem ) {
+			return rheader.test( elem.nodeName );
+		},
+
+		"input": function( elem ) {
+			return rinputs.test( elem.nodeName );
+		},
+
+		"button": function( elem ) {
+			var name = elem.nodeName.toLowerCase();
+			return name === "input" && elem.type === "button" || name === "button";
+		},
+
+		"text": function( elem ) {
+			var attr;
+			return elem.nodeName.toLowerCase() === "input" &&
+				elem.type === "text" &&
+
+				// Support: IE<8
+				// New HTML5 attribute values (e.g., "search") appear with elem.type === "text"
+				( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === "text" );
+		},
+
+		// Position-in-collection
+		"first": createPositionalPseudo(function() {
+			return [ 0 ];
+		}),
+
+		"last": createPositionalPseudo(function( matchIndexes, length ) {
+			return [ length - 1 ];
+		}),
+
+		"eq": createPositionalPseudo(function( matchIndexes, length, argument ) {
+			return [ argument < 0 ? argument + length : argument ];
+		}),
+
+		"even": createPositionalPseudo(function( matchIndexes, length ) {
+			var i = 0;
+			for ( ; i < length; i += 2 ) {
+				matchIndexes.push( i );
+			}
+			return matchIndexes;
+		}),
+
+		"odd": createPositionalPseudo(function( matchIndexes, length ) {
+			var i = 1;
+			for ( ; i < length; i += 2 ) {
+				matchIndexes.push( i );
+			}
+			return matchIndexes;
+		}),
+
+		"lt": createPositionalPseudo(function( matchIndexes, length, argument ) {
+			var i = argument < 0 ? argument + length : argument;
+			for ( ; --i >= 0; ) {
+				matchIndexes.push( i );
+			}
+			return matchIndexes;
+		}),
+
+		"gt": createPositionalPseudo(function( matchIndexes, length, argument ) {
+			var i = argument < 0 ? argument + length : argument;
+			for ( ; ++i < length; ) {
+				matchIndexes.push( i );
+			}
+			return matchIndexes;
+		})
+	}
+};
+
+Expr.pseudos["nth"] = Expr.pseudos["eq"];
+
+// Add button/input type pseudos
+for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) {
+	Expr.pseudos[ i ] = createInputPseudo( i );
+}
+for ( i in { submit: true, reset: true } ) {
+	Expr.pseudos[ i ] = createButtonPseudo( i );
+}
+
+// Easy API for creating new setFilters
+function setFilters() {}
+setFilters.prototype = Expr.filters = Expr.pseudos;
+Expr.setFilters = new setFilters();
+
+tokenize = Sizzle.tokenize = function( selector, parseOnly ) {
+	var matched, match, tokens, type,
+		soFar, groups, preFilters,
+		cached = tokenCache[ selector + " " ];
+
+	if ( cached ) {
+		return parseOnly ? 0 : cached.slice( 0 );
+	}
+
+	soFar = selector;
+	groups = [];
+	preFilters = Expr.preFilter;
+
+	while ( soFar ) {
+
+		// Comma and first run
+		if ( !matched || (match = rcomma.exec( soFar )) ) {
+			if ( match ) {
+				// Don't consume trailing commas as valid
+				soFar = soFar.slice( match[0].length ) || soFar;
+			}
+			groups.push( (tokens = []) );
+		}
+
+		matched = false;
+
+		// Combinators
+		if ( (match = rcombinators.exec( soFar )) ) {
+			matched = match.shift();
+			tokens.push({
+				value: matched,
+				// Cast descendant combinators to space
+				type: match[0].replace( rtrim, " " )
+			});
+			soFar = soFar.slice( matched.length );
+		}
+
+		// Filters
+		for ( type in Expr.filter ) {
+			if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] ||
+				(match = preFilters[ type ]( match ))) ) {
+				matched = match.shift();
+				tokens.push({
+					value: matched,
+					type: type,
+					matches: match
+				});
+				soFar = soFar.slice( matched.length );
+			}
+		}
+
+		if ( !matched ) {
+			break;
+		}
+	}
+
+	// Return the length of the invalid excess
+	// if we're just parsing
+	// Otherwise, throw an error or return tokens
+	return parseOnly ?
+		soFar.length :
+		soFar ?
+			Sizzle.error( selector ) :
+			// Cache the tokens
+			tokenCache( selector, groups ).slice( 0 );
+};
+
+function toSelector( tokens ) {
+	var i = 0,
+		len = tokens.length,
+		selector = "";
+	for ( ; i < len; i++ ) {
+		selector += tokens[i].value;
+	}
+	return selector;
+}
+
+function addCombinator( matcher, combinator, base ) {
+	var dir = combinator.dir,
+		checkNonElements = base && dir === "parentNode",
+		doneName = done++;
+
+	return combinator.first ?
+		// Check against closest ancestor/preceding element
+		function( elem, context, xml ) {
+			while ( (elem = elem[ dir ]) ) {
+				if ( elem.nodeType === 1 || checkNonElements ) {
+					return matcher( elem, context, xml );
+				}
+			}
+		} :
+
+		// Check against all ancestor/preceding elements
+		function( elem, context, xml ) {
+			var oldCache, outerCache,
+				newCache = [ dirruns, doneName ];
+
+			// We can't set arbitrary data on XML nodes, so they don't benefit from dir caching
+			if ( xml ) {
+				while ( (elem = elem[ dir ]) ) {
+					if ( elem.nodeType === 1 || checkNonElements ) {
+						if ( matcher( elem, context, xml ) ) {
+							return true;
+						}
+					}
+				}
+			} else {
+				while ( (elem = elem[ dir ]) ) {
+					if ( elem.nodeType === 1 || checkNonElements ) {
+						outerCache = elem[ expando ] || (elem[ expando ] = {});
+						if ( (oldCache = outerCache[ dir ]) &&
+							oldCache[ 0 ] === dirruns && oldCache[ 1 ] === doneName ) {
+
+							// Assign to newCache so results back-propagate to previous elements
+							return (newCache[ 2 ] = oldCache[ 2 ]);
+						} else {
+							// Reuse newcache so results back-propagate to previous elements
+							outerCache[ dir ] = newCache;
+
+							// A match means we're done; a fail means we have to keep checking
+							if ( (newCache[ 2 ] = matcher( elem, context, xml )) ) {
+								return true;
+							}
+						}
+					}
+				}
+			}
+		};
+}
+
+function elementMatcher( matchers ) {
+	return matchers.length > 1 ?
+		function( elem, context, xml ) {
+			var i = matchers.length;
+			while ( i-- ) {
+				if ( !matchers[i]( elem, context, xml ) ) {
+					return false;
+				}
+			}
+			return true;
+		} :
+		matchers[0];
+}
+
+function multipleContexts( selector, contexts, results ) {
+	var i = 0,
+		len = contexts.length;
+	for ( ; i < len; i++ ) {
+		Sizzle( selector, contexts[i], results );
+	}
+	return results;
+}
+
+function condense( unmatched, map, filter, context, xml ) {
+	var elem,
+		newUnmatched = [],
+		i = 0,
+		len = unmatched.length,
+		mapped = map != null;
+
+	for ( ; i < len; i++ ) {
+		if ( (elem = unmatched[i]) ) {
+			if ( !filter || filter( elem, context, xml ) ) {
+				newUnmatched.push( elem );
+				if ( mapped ) {
+					map.push( i );
+				}
+			}
+		}
+	}
+
+	return newUnmatched;
+}
+
+function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) {
+	if ( postFilter && !postFilter[ expando ] ) {
+		postFilter = setMatcher( postFilter );
+	}
+	if ( postFinder && !postFinder[ expando ] ) {
+		postFinder = setMatcher( postFinder, postSelector );
+	}
+	return markFunction(function( seed, results, context, xml ) {
+		var temp, i, elem,
+			preMap = [],
+			postMap = [],
+			preexisting = results.length,
+
+			// Get initial elements from seed or context
+			elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [] ),
+
+			// Prefilter to get matcher input, preserving a map for seed-results synchronization
+			matcherIn = preFilter && ( seed || !selector ) ?
+				condense( elems, preMap, preFilter, context, xml ) :
+				elems,
+
+			matcherOut = matcher ?
+				// If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results,
+				postFinder || ( seed ? preFilter : preexisting || postFilter ) ?
+
+					// ...intermediate processing is necessary
+					[] :
+
+					// ...otherwise use results directly
+					results :
+				matcherIn;
+
+		// Find primary matches
+		if ( matcher ) {
+			matcher( matcherIn, matcherOut, context, xml );
+		}
+
+		// Apply postFilter
+		if ( postFilter ) {
+			temp = condense( matcherOut, postMap );
+			postFilter( temp, [], context, xml );
+
+			// Un-match failing elements by moving them back to matcherIn
+			i = temp.length;
+			while ( i-- ) {
+				if ( (elem = temp[i]) ) {
+					matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem);
+				}
+			}
+		}
+
+		if ( seed ) {
+			if ( postFinder || preFilter ) {
+				if ( postFinder ) {
+					// Get the final matcherOut by condensing this intermediate into postFinder contexts
+					temp = [];
+					i = matcherOut.length;
+					while ( i-- ) {
+						if ( (elem = matcherOut[i]) ) {
+							// Restore matcherIn since elem is not yet a final match
+							temp.push( (matcherIn[i] = elem) );
+						}
+					}
+					postFinder( null, (matcherOut = []), temp, xml );
+				}
+
+				// Move matched elements from seed to results to keep them synchronized
+				i = matcherOut.length;
+				while ( i-- ) {
+					if ( (elem = matcherOut[i]) &&
+						(temp = postFinder ? indexOf.call( seed, elem ) : preMap[i]) > -1 ) {
+
+						seed[temp] = !(results[temp] = elem);
+					}
+				}
+			}
+
+		// Add elements to results, through postFinder if defined
+		} else {
+			matcherOut = condense(
+				matcherOut === results ?
+					matcherOut.splice( preexisting, matcherOut.length ) :
+					matcherOut
+			);
+			if ( postFinder ) {
+				postFinder( null, results, matcherOut, xml );
+			} else {
+				push.apply( results, matcherOut );
+			}
+		}
+	});
+}
+
+function matcherFromTokens( tokens ) {
+	var checkContext, matcher, j,
+		len = tokens.length,
+		leadingRelative = Expr.relative[ tokens[0].type ],
+		implicitRelative = leadingRelative || Expr.relative[" "],
+		i = leadingRelative ? 1 : 0,
+
+		// The foundational matcher ensures that elements are reachable from top-level context(s)
+		matchContext = addCombinator( function( elem ) {
+			return elem === checkContext;
+		}, implicitRelative, true ),
+		matchAnyContext = addCombinator( function( elem ) {
+			return indexOf.call( checkContext, elem ) > -1;
+		}, implicitRelative, true ),
+		matchers = [ function( elem, context, xml ) {
+			return ( !leadingRelative && ( xml || context !== outermostContext ) ) || (
+				(checkContext = context).nodeType ?
+					matchContext( elem, context, xml ) :
+					matchAnyContext( elem, context, xml ) );
+		} ];
+
+	for ( ; i < len; i++ ) {
+		if ( (matcher = Expr.relative[ tokens[i].type ]) ) {
+			matchers = [ addCombinator(elementMatcher( matchers ), matcher) ];
+		} else {
+			matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches );
+
+			// Return special upon seeing a positional matcher
+			if ( matcher[ expando ] ) {
+				// Find the next relative operator (if any) for proper handling
+				j = ++i;
+				for ( ; j < len; j++ ) {
+					if ( Expr.relative[ tokens[j].type ] ) {
+						break;
+					}
+				}
+				return setMatcher(
+					i > 1 && elementMatcher( matchers ),
+					i > 1 && toSelector(
+						// If the preceding token was a descendant combinator, insert an implicit any-element `*`
+						tokens.slice( 0, i - 1 ).concat({ value: tokens[ i - 2 ].type === " " ? "*" : "" })
+					).replace( rtrim, "$1" ),
+					matcher,
+					i < j && matcherFromTokens( tokens.slice( i, j ) ),
+					j < len && matcherFromTokens( (tokens = tokens.slice( j )) ),
+					j < len && toSelector( tokens )
+				);
+			}
+			matchers.push( matcher );
+		}
+	}
+
+	return elementMatcher( matchers );
+}
+
+function matcherFromGroupMatchers( elementMatchers, setMatchers ) {
+	var bySet = setMatchers.length > 0,
+		byElement = elementMatchers.length > 0,
+		superMatcher = function( seed, context, xml, results, outermost ) {
+			var elem, j, matcher,
+				matchedCount = 0,
+				i = "0",
+				unmatched = seed && [],
+				setMatched = [],
+				contextBackup = outermostContext,
+				// We must always have either seed elements or outermost context
+				elems = seed || byElement && Expr.find["TAG"]( "*", outermost ),
+				// Use integer dirruns iff this is the outermost matcher
+				dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.random() || 0.1),
+				len = elems.length;
+
+			if ( outermost ) {
+				outermostContext = context !== document && context;
+			}
+
+			// Add elements passing elementMatchers directly to results
+			// Keep `i` a string if there are no elements so `matchedCount` will be "00" below
+			// Support: IE<9, Safari
+			// Tolerate NodeList properties (IE: "length"; Safari: <number>) matching elements by id
+			for ( ; i !== len && (elem = elems[i]) != null; i++ ) {
+				if ( byElement && elem ) {
+					j = 0;
+					while ( (matcher = elementMatchers[j++]) ) {
+						if ( matcher( elem, context, xml ) ) {
+							results.push( elem );
+							break;
+						}
+					}
+					if ( outermost ) {
+						dirruns = dirrunsUnique;
+					}
+				}
+
+				// Track unmatched elements for set filters
+				if ( bySet ) {
+					// They will have gone through all possible matchers
+					if ( (elem = !matcher && elem) ) {
+						matchedCount--;
+					}
+
+					// Lengthen the array for every element, matched or not
+					if ( seed ) {
+						unmatched.push( elem );
+					}
+				}
+			}
+
+			// Apply set filters to unmatched elements
+			matchedCount += i;
+			if ( bySet && i !== matchedCount ) {
+				j = 0;
+				while ( (matcher = setMatchers[j++]) ) {
+					matcher( unmatched, setMatched, context, xml );
+				}
+
+				if ( seed ) {
+					// Reintegrate element matches to eliminate the need for sorting
+					if ( matchedCount > 0 ) {
+						while ( i-- ) {
+							if ( !(unmatched[i] || setMatched[i]) ) {
+								setMatched[i] = pop.call( results );
+							}
+						}
+					}
+
+					// Discard index placeholder values to get only actual matches
+					setMatched = condense( setMatched );
+				}
+
+				// Add matches to results
+				push.apply( results, setMatched );
+
+				// Seedless set matches succeeding multiple successful matchers stipulate sorting
+				if ( outermost && !seed && setMatched.length > 0 &&
+					( matchedCount + setMatchers.length ) > 1 ) {
+
+					Sizzle.uniqueSort( results );
+				}
+			}
+
+			// Override manipulation of globals by nested matchers
+			if ( outermost ) {
+				dirruns = dirrunsUnique;
+				outermostContext = contextBackup;
+			}
+
+			return unmatched;
+		};
+
+	return bySet ?
+		markFunction( superMatcher ) :
+		superMatcher;
+}
+
+compile = Sizzle.compile = function( selector, match /* Internal Use Only */ ) {
+	var i,
+		setMatchers = [],
+		elementMatchers = [],
+		cached = compilerCache[ selector + " " ];
+
+	if ( !cached ) {
+		// Generate a function of recursive functions that can be used to check each element
+		if ( !match ) {
+			match = tokenize( selector );
+		}
+		i = match.length;
+		while ( i-- ) {
+			cached = matcherFromTokens( match[i] );
+			if ( cached[ expando ] ) {
+				setMatchers.push( cached );
+			} else {
+				elementMatchers.push( cached );
+			}
+		}
+
+		// Cache the compiled function
+		cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) );
+
+		// Save selector and tokenization
+		cached.selector = selector;
+	}
+	return cached;
+};
+
+/**
+ * A low-level selection function that works with Sizzle's compiled
+ *  selector functions
+ * @param {String|Function} selector A selector or a pre-compiled
+ *  selector function built with Sizzle.compile
+ * @param {Element} context
+ * @param {Array} [results]
+ * @param {Array} [seed] A set of elements to match against
+ */
+select = Sizzle.select = function( selector, context, results, seed ) {
+	var i, tokens, token, type, find,
+		compiled = typeof selector === "function" && selector,
+		match = !seed && tokenize( (selector = compiled.selector || selector) );
+
+	results = results || [];
+
+	// Try to minimize operations if there is no seed and only one group
+	if ( match.length === 1 ) {
+
+		// Take a shortcut and set the context if the root selector is an ID
+		tokens = match[0] = match[0].slice( 0 );
+		if ( tokens.length > 2 && (token = tokens[0]).type === "ID" &&
+				support.getById && context.nodeType === 9 && documentIsHTML &&
+				Expr.relative[ tokens[1].type ] ) {
+
+			context = ( Expr.find["ID"]( token.matches[0].replace(runescape, funescape), context ) || [] )[0];
+			if ( !context ) {
+				return results;
+
+			// Precompiled matchers will still verify ancestry, so step up a level
+			} else if ( compiled ) {
+				context = context.parentNode;
+			}
+
+			selector = selector.slice( tokens.shift().value.length );
+		}
+
+		// Fetch a seed set for right-to-left matching
+		i = matchExpr["needsContext"].test( selector ) ? 0 : tokens.length;
+		while ( i-- ) {
+			token = tokens[i];
+
+			// Abort if we hit a combinator
+			if ( Expr.relative[ (type = token.type) ] ) {
+				break;
+			}
+			if ( (find = Expr.find[ type ]) ) {
+				// Search, expanding context for leading sibling combinators
+				if ( (seed = find(
+					token.matches[0].replace( runescape, funescape ),
+					rsibling.test( tokens[0].type ) && testContext( context.parentNode ) || context
+				)) ) {
+
+					// If seed is empty or no tokens remain, we can return early
+					tokens.splice( i, 1 );
+					selector = seed.length && toSelector( tokens );
+					if ( !selector ) {
+						push.apply( results, seed );
+						return results;
+					}
+
+					break;
+				}
+			}
+		}
+	}
+
+	// Compile and execute a filtering function if one is not provided
+	// Provide `match` to avoid retokenization if we modified the selector above
+	( compiled || compile( selector, match ) )(
+		seed,
+		context,
+		!documentIsHTML,
+		results,
+		rsibling.test( selector ) && testContext( context.parentNode ) || context
+	);
+	return results;
+};
+
+// One-time assignments
+
+// Sort stability
+support.sortStable = expando.split("").sort( sortOrder ).join("") === expando;
+
+// Support: Chrome<14
+// Always assume duplicates if they aren't passed to the comparison function
+support.detectDuplicates = !!hasDuplicate;
+
+// Initialize against the default document
+setDocument();
+
+// Support: Webkit<537.32 - Safari 6.0.3/Chrome 25 (fixed in Chrome 27)
+// Detached nodes confoundingly follow *each other*
+support.sortDetached = assert(function( div1 ) {
+	// Should return 1, but returns 4 (following)
+	return div1.compareDocumentPosition( document.createElement("div") ) & 1;
+});
+
+// Support: IE<8
+// Prevent attribute/property "interpolation"
+// http://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx
+if ( !assert(function( div ) {
+	div.innerHTML = "<a href='#'></a>";
+	return div.firstChild.getAttribute("href") === "#" ;
+}) ) {
+	addHandle( "type|href|height|width", function( elem, name, isXML ) {
+		if ( !isXML ) {
+			return elem.getAttribute( name, name.toLowerCase() === "type" ? 1 : 2 );
+		}
+	});
+}
+
+// Support: IE<9
+// Use defaultValue in place of getAttribute("value")
+if ( !support.attributes || !assert(function( div ) {
+	div.innerHTML = "<input/>";
+	div.firstChild.setAttribute( "value", "" );
+	return div.firstChild.getAttribute( "value" ) === "";
+}) ) {
+	addHandle( "value", function( elem, name, isXML ) {
+		if ( !isXML && elem.nodeName.toLowerCase() === "input" ) {
+			return elem.defaultValue;
+		}
+	});
+}
+
+// Support: IE<9
+// Use getAttributeNode to fetch booleans when getAttribute lies
+if ( !assert(function( div ) {
+	return div.getAttribute("disabled") == null;
+}) ) {
+	addHandle( booleans, function( elem, name, isXML ) {
+		var val;
+		if ( !isXML ) {
+			return elem[ name ] === true ? name.toLowerCase() :
+					(val = elem.getAttributeNode( name )) && val.specified ?
+					val.value :
+				null;
+		}
+	});
+}
+
+return Sizzle;
+
+})( window );
+
+
+
+jQuery.find = Sizzle;
+jQuery.expr = Sizzle.selectors;
+jQuery.expr[":"] = jQuery.expr.pseudos;
+jQuery.unique = Sizzle.uniqueSort;
+jQuery.text = Sizzle.getText;
+jQuery.isXMLDoc = Sizzle.isXML;
+jQuery.contains = Sizzle.contains;
+
+
+
+var rneedsContext = jQuery.expr.match.needsContext;
+
+var rsingleTag = (/^<(\w+)\s*\/?>(?:<\/\1>|)$/);
+
+
+
+var risSimple = /^.[^:#\[\.,]*$/;
+
+// Implement the identical functionality for filter and not
+function winnow( elements, qualifier, not ) {
+	if ( jQuery.isFunction( qualifier ) ) {
+		return jQuery.grep( elements, function( elem, i ) {
+			/* jshint -W018 */
+			return !!qualifier.call( elem, i, elem ) !== not;
+		});
+
+	}
+
+	if ( qualifier.nodeType ) {
+		return jQuery.grep( elements, function( elem ) {
+			return ( elem === qualifier ) !== not;
+		});
+
+	}
+
+	if ( typeof qualifier === "string" ) {
+		if ( risSimple.test( qualifier ) ) {
+			return jQuery.filter( qualifier, elements, not );
+		}
+
+		qualifier = jQuery.filter( qualifier, elements );
+	}
+
+	return jQuery.grep( elements, function( elem ) {
+		return ( indexOf.call( qualifier, elem ) >= 0 ) !== not;
+	});
+}
+
+jQuery.filter = function( expr, elems, not ) {
+	var elem = elems[ 0 ];
+
+	if ( not ) {
+		expr = ":not(" + expr + ")";
+	}
+
+	return elems.length === 1 && elem.nodeType === 1 ?
+		jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : [] :
+		jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) {
+			return elem.nodeType === 1;
+		}));
+};
+
+jQuery.fn.extend({
+	find: function( selector ) {
+		var i,
+			len = this.length,
+			ret = [],
+			self = this;
+
+		if ( typeof selector !== "string" ) {
+			return this.pushStack( jQuery( selector ).filter(function() {
+				for ( i = 0; i < len; i++ ) {
+					if ( jQuery.contains( self[ i ], this ) ) {
+						return true;
+					}
+				}
+			}) );
+		}
+
+		for ( i = 0; i < len; i++ ) {
+			jQuery.find( selector, self[ i ], ret );
+		}
+
+		// Needed because $( selector, context ) becomes $( context ).find( selector )
+		ret = this.pushStack( len > 1 ? jQuery.unique( ret ) : ret );
+		ret.selector = this.selector ? this.selector + " " + selector : selector;
+		return ret;
+	},
+	filter: function( selector ) {
+		return this.pushStack( winnow(this, selector || [], false) );
+	},
+	not: function( selector ) {
+		return this.pushStack( winnow(this, selector || [], true) );
+	},
+	is: function( selector ) {
+		return !!winnow(
+			this,
+
+			// If this is a positional/relative selector, check membership in the returned set
+			// so $("p:first").is("p:last") won't return true for a doc with two "p".
+			typeof selector === "string" && rneedsContext.test( selector ) ?
+				jQuery( selector ) :
+				selector || [],
+			false
+		).length;
+	}
+});
+
+
+// Initialize a jQuery object
+
+
+// A central reference to the root jQuery(document)
+var rootjQuery,
+
+	// A simple way to check for HTML strings
+	// Prioritize #id over <tag> to avoid XSS via location.hash (#9521)
+	// Strict HTML recognition (#11290: must start with <)
+	rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,
+
+	init = jQuery.fn.init = function( selector, context ) {
+		var match, elem;
+
+		// HANDLE: $(""), $(null), $(undefined), $(false)
+		if ( !selector ) {
+			return this;
+		}
+
+		// Handle HTML strings
+		if ( typeof selector === "string" ) {
+			if ( selector[0] === "<" && selector[ selector.length - 1 ] === ">" && selector.length >= 3 ) {
+				// Assume that strings that start and end with <> are HTML and skip the regex check
+				match = [ null, selector, null ];
+
+			} else {
+				match = rquickExpr.exec( selector );
+			}
+
+			// Match html or make sure no context is specified for #id
+			if ( match && (match[1] || !context) ) {
+
+				// HANDLE: $(html) -> $(array)
+				if ( match[1] ) {
+					context = context instanceof jQuery ? context[0] : context;
+
+					// scripts is true for back-compat
+					// Intentionally let the error be thrown if parseHTML is not present
+					jQuery.merge( this, jQuery.parseHTML(
+						match[1],
+						context && context.nodeType ? context.ownerDocument || context : document,
+						true
+					) );
+
+					// HANDLE: $(html, props)
+					if ( rsingleTag.test( match[1] ) && jQuery.isPlainObject( context ) ) {
+						for ( match in context ) {
+							// Properties of context are called as methods if possible
+							if ( jQuery.isFunction( this[ match ] ) ) {
+								this[ match ]( context[ match ] );
+
+							// ...and otherwise set as attributes
+							} else {
+								this.attr( match, context[ match ] );
+							}
+						}
+					}
+
+					return this;
+
+				// HANDLE: $(#id)
+				} else {
+					elem = document.getElementById( match[2] );
+
+					// Check parentNode to catch when Blackberry 4.6 returns
+					// nodes that are no longer in the document #6963
+					if ( elem && elem.parentNode ) {
+						// Inject the element directly into the jQuery object
+						this.length = 1;
+						this[0] = elem;
+					}
+
+					this.context = document;
+					this.selector = selector;
+					return this;
+				}
+
+			// HANDLE: $(expr, $(...))
+			} else if ( !context || context.jquery ) {
+				return ( context || rootjQuery ).find( selector );
+
+			// HANDLE: $(expr, context)
+			// (which is just equivalent to: $(context).find(expr)
+			} else {
+				return this.constructor( context ).find( selector );
+			}
+
+		// HANDLE: $(DOMElement)
+		} else if ( selector.nodeType ) {
+			this.context = this[0] = selector;
+			this.length = 1;
+			return this;
+
+		// HANDLE: $(function)
+		// Shortcut for document ready
+		} else if ( jQuery.isFunction( selector ) ) {
+			return typeof rootjQuery.ready !== "undefined" ?
+				rootjQuery.ready( selector ) :
+				// Execute immediately if ready is not present
+				selector( jQuery );
+		}
+
+		if ( selector.selector !== undefined ) {
+			this.selector = selector.selector;
+			this.context = selector.context;
+		}
+
+		return jQuery.makeArray( selector, this );
+	};
+
+// Give the init function the jQuery prototype for later instantiation
+init.prototype = jQuery.fn;
+
+// Initialize central reference
+rootjQuery = jQuery( document );
+
+
+var rparentsprev = /^(?:parents|prev(?:Until|All))/,
+	// methods guaranteed to produce a unique set when starting from a unique set
+	guaranteedUnique = {
+		children: true,
+		contents: true,
+		next: true,
+		prev: true
+	};
+
+jQuery.extend({
+	dir: function( elem, dir, until ) {
+		var matched = [],
+			truncate = until !== undefined;
+
+		while ( (elem = elem[ dir ]) && elem.nodeType !== 9 ) {
+			if ( elem.nodeType === 1 ) {
+				if ( truncate && jQuery( elem ).is( until ) ) {
+					break;
+				}
+				matched.push( elem );
+			}
+		}
+		return matched;
+	},
+
+	sibling: function( n, elem ) {
+		var matched = [];
+
+		for ( ; n; n = n.nextSibling ) {
+			if ( n.nodeType === 1 && n !== elem ) {
+				matched.push( n );
+			}
+		}
+
+		return matched;
+	}
+});
+
+jQuery.fn.extend({
+	has: function( target ) {
+		var targets = jQuery( target, this ),
+			l = targets.length;
+
+		return this.filter(function() {
+			var i = 0;
+			for ( ; i < l; i++ ) {
+				if ( jQuery.contains( this, targets[i] ) ) {
+					return true;
+				}
+			}
+		});
+	},
+
+	closest: function( selectors, context ) {
+		var cur,
+			i = 0,
+			l = this.length,
+			matched = [],
+			pos = rneedsContext.test( selectors ) || typeof selectors !== "string" ?
+				jQuery( selectors, context || this.context ) :
+				0;
+
+		for ( ; i < l; i++ ) {
+			for ( cur = this[i]; cur && cur !== context; cur = cur.parentNode ) {
+				// Always skip document fragments
+				if ( cur.nodeType < 11 && (pos ?
+					pos.index(cur) > -1 :
+
+					// Don't pass non-elements to Sizzle
+					cur.nodeType === 1 &&
+						jQuery.find.matchesSelector(cur, selectors)) ) {
+
+					matched.push( cur );
+					break;
+				}
+			}
+		}
+
+		return this.pushStack( matched.length > 1 ? jQuery.unique( matched ) : matched );
+	},
+
+	// Determine the position of an element within
+	// the matched set of elements
+	index: function( elem ) {
+
+		// No argument, return index in parent
+		if ( !elem ) {
+			return ( this[ 0 ] && this[ 0 ].parentNode ) ? this.first().prevAll().length : -1;
+		}
+
+		// index in selector
+		if ( typeof elem === "string" ) {
+			return indexOf.call( jQuery( elem ), this[ 0 ] );
+		}
+
+		// Locate the position of the desired element
+		return indexOf.call( this,
+
+			// If it receives a jQuery object, the first element is used
+			elem.jquery ? elem[ 0 ] : elem
+		);
+	},
+
+	add: function( selector, context ) {
+		return this.pushStack(
+			jQuery.unique(
+				jQuery.merge( this.get(), jQuery( selector, context ) )
+			)
+		);
+	},
+
+	addBack: function( selector ) {
+		return this.add( selector == null ?
+			this.prevObject : this.prevObject.filter(selector)
+		);
+	}
+});
+
+function sibling( cur, dir ) {
+	while ( (cur = cur[dir]) && cur.nodeType !== 1 ) {}
+	return cur;
+}
+
+jQuery.each({
+	parent: function( elem ) {
+		var parent = elem.parentNode;
+		return parent && parent.nodeType !== 11 ? parent : null;
+	},
+	parents: function( elem ) {
+		return jQuery.dir( elem, "parentNode" );
+	},
+	parentsUntil: function( elem, i, until ) {
+		return jQuery.dir( elem, "parentNode", until );
+	},
+	next: function( elem ) {
+		return sibling( elem, "nextSibling" );
+	},
+	prev: function( elem ) {
+		return sibling( elem, "previousSibling" );
+	},
+	nextAll: function( elem ) {
+		return jQuery.dir( elem, "nextSibling" );
+	},
+	prevAll: function( elem ) {
+		return jQuery.dir( elem, "previousSibling" );
+	},
+	nextUntil: function( elem, i, until ) {
+		return jQuery.dir( elem, "nextSibling", until );
+	},
+	prevUntil: function( elem, i, until ) {
+		return jQuery.dir( elem, "previousSibling", until );
+	},
+	siblings: function( elem ) {
+		return jQuery.sibling( ( elem.parentNode || {} ).firstChild, elem );
+	},
+	children: function( elem ) {
+		return jQuery.sibling( elem.firstChild );
+	},
+	contents: function( elem ) {
+		return elem.contentDocument || jQuery.merge( [], elem.childNodes );
+	}
+}, function( name, fn ) {
+	jQuery.fn[ name ] = function( until, selector ) {
+		var matched = jQuery.map( this, fn, until );
+
+		if ( name.slice( -5 ) !== "Until" ) {
+			selector = until;
+		}
+
+		if ( selector && typeof selector === "string" ) {
+			matched = jQuery.filter( selector, matched );
+		}
+
+		if ( this.length > 1 ) {
+			// Remove duplicates
+			if ( !guaranteedUnique[ name ] ) {
+				jQuery.unique( matched );
+			}
+
+			// Reverse order for parents* and prev-derivatives
+			if ( rparentsprev.test( name ) ) {
+				matched.reverse();
+			}
+		}
+
+		return this.pushStack( matched );
+	};
+});
+var rnotwhite = (/\S+/g);
+
+
+
+// String to Object options format cache
+var optionsCache = {};
+
+// Convert String-formatted options into Object-formatted ones and store in cache
+function createOptions( options ) {
+	var object = optionsCache[ options ] = {};
+	jQuery.each( options.match( rnotwhite ) || [], function( _, flag ) {
+		object[ flag ] = true;
+	});
+	return object;
+}
+
+/*
+ * Create a callback list using the following parameters:
+ *
+ *	options: an optional list of space-separated options that will change how
+ *			the callback list behaves or a more traditional option object
+ *
+ * By default a callback list will act like an event callback list and can be
+ * "fired" multiple times.
+ *
+ * Possible options:
+ *
+ *	once:			will ensure the callback list can only be fired once (like a Deferred)
+ *
+ *	memory:			will keep track of previous values and will call any callback added
+ *					after the list has been fired right away with the latest "memorized"
+ *					values (like a Deferred)
+ *
+ *	unique:			will ensure a callback can only be added once (no duplicate in the list)
+ *
+ *	stopOnFalse:	interrupt callings when a callback returns false
+ *
+ */
+jQuery.Callbacks = function( options ) {
+
+	// Convert options from String-formatted to Object-formatted if needed
+	// (we check in cache first)
+	options = typeof options === "string" ?
+		( optionsCache[ options ] || createOptions( options ) ) :
+		jQuery.extend( {}, options );
+
+	var // Last fire value (for non-forgettable lists)
+		memory,
+		// Flag to know if list was already fired
+		fired,
+		// Flag to know if list is currently firing
+		firing,
+		// First callback to fire (used internally by add and fireWith)
+		firingStart,
+		// End of the loop when firing
+		firingLength,
+		// Index of currently firing callback (modified by remove if needed)
+		firingIndex,
+		// Actual callback list
+		list = [],
+		// Stack of fire calls for repeatable lists
+		stack = !options.once && [],
+		// Fire callbacks
+		fire = function( data ) {
+			memory = options.memory && data;
+			fired = true;
+			firingIndex = firingStart || 0;
+			firingStart = 0;
+			firingLength = list.length;
+			firing = true;
+			for ( ; list && firingIndex < firingLength; firingIndex++ ) {
+				if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) {
+					memory = false; // To prevent further calls using add
+					break;
+				}
+			}
+			firing = false;
+			if ( list ) {
+				if ( stack ) {
+					if ( stack.length ) {
+						fire( stack.shift() );
+					}
+				} else if ( memory ) {
+					list = [];
+				} else {
+					self.disable();
+				}
+			}
+		},
+		// Actual Callbacks object
+		self = {
+			// Add a callback or a collection of callbacks to the list
+			add: function() {
+				if ( list ) {
+					// First, we save the current length
+					var start = list.length;
+					(function add( args ) {
+						jQuery.each( args, function( _, arg ) {
+							var type = jQuery.type( arg );
+							if ( type === "function" ) {
+								if ( !options.unique || !self.has( arg ) ) {
+									list.push( arg );
+								}
+							} else if ( arg && arg.length && type !== "string" ) {
+								// Inspect recursively
+								add( arg );
+							}
+						});
+					})( arguments );
+					// Do we need to add the callbacks to the
+					// current firing batch?
+					if ( firing ) {
+						firingLength = list.length;
+					// With memory, if we're not firing then
+					// we should call right away
+					} else if ( memory ) {
+						firingStart = start;
+						fire( memory );
+					}
+				}
+				return this;
+			},
+			// Remove a callback from the list
+			remove: function() {
+				if ( list ) {
+					jQuery.each( arguments, function( _, arg ) {
+						var index;
+						while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) {
+							list.splice( index, 1 );
+							// Handle firing indexes
+							if ( firing ) {
+								if ( index <= firingLength ) {
+									firingLength--;
+								}
+								if ( index <= firingIndex ) {
+									firingIndex--;
+								}
+							}
+						}
+					});
+				}
+				return this;
+			},
+			// Check if a given callback is in the list.
+			// If no argument is given, return whether or not list has callbacks attached.
+			has: function( fn ) {
+				return fn ? jQuery.inArray( fn, list ) > -1 : !!( list && list.length );
+			},
+			// Remove all callbacks from the list
+			empty: function() {
+				list = [];
+				firingLength = 0;
+				return this;
+			},
+			// Have the list do nothing anymore
+			disable: function() {
+				list = stack = memory = undefined;
+				return this;
+			},
+			// Is it disabled?
+			disabled: function() {
+				return !list;
+			},
+			// Lock the list in its current state
+			lock: function() {
+				stack = undefined;
+				if ( !memory ) {
+					self.disable();
+				}
+				return this;
+			},
+			// Is it locked?
+			locked: function() {
+				return !stack;
+			},
+			// Call all callbacks with the given context and arguments
+			fireWith: function( context, args ) {
+				if ( list && ( !fired || stack ) ) {
+					args = args || [];
+					args = [ context, args.slice ? args.slice() : args ];
+					if ( firing ) {
+						stack.push( args );
+					} else {
+						fire( args );
+					}
+				}
+				return this;
+			},
+			// Call all the callbacks with the given arguments
+			fire: function() {
+				self.fireWith( this, arguments );
+				return this;
+			},
+			// To know if the callbacks have already been called at least once
+			fired: function() {
+				return !!fired;
+			}
+		};
+
+	return self;
+};
+
+
+jQuery.extend({
+
+	Deferred: function( func ) {
+		var tuples = [
+				// action, add listener, listener list, final state
+				[ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ],
+				[ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ],
+				[ "notify", "progress", jQuery.Callbacks("memory") ]
+			],
+			state = "pending",
+			promise = {
+				state: function() {
+					return state;
+				},
+				always: function() {
+					deferred.done( arguments ).fail( arguments );
+					return this;
+				},
+				then: function( /* fnDone, fnFail, fnProgress */ ) {
+					var fns = arguments;
+					return jQuery.Deferred(function( newDefer ) {
+						jQuery.each( tuples, function( i, tuple ) {
+							var fn = jQuery.isFunction( fns[ i ] ) && fns[ i ];
+							// deferred[ done | fail | progress ] for forwarding actions to newDefer
+							deferred[ tuple[1] ](function() {
+								var returned = fn && fn.apply( this, arguments );
+								if ( returned && jQuery.isFunction( returned.promise ) ) {
+									returned.promise()
+										.done( newDefer.resolve )
+										.fail( newDefer.reject )
+										.progress( newDefer.notify );
+								} else {
+									newDefer[ tuple[ 0 ] + "With" ]( this === promise ? newDefer.promise() : this, fn ? [ returned ] : arguments );
+								}
+							});
+						});
+						fns = null;
+					}).promise();
+				},
+				// Get a promise for this deferred
+				// If obj is provided, the promise aspect is added to the object
+				promise: function( obj ) {
+					return obj != null ? jQuery.extend( obj, promise ) : promise;
+				}
+			},
+			deferred = {};
+
+		// Keep pipe for back-compat
+		promise.pipe = promise.then;
+
+		// Add list-specific methods
+		jQuery.each( tuples, function( i, tuple ) {
+			var list = tuple[ 2 ],
+				stateString = tuple[ 3 ];
+
+			// promise[ done | fail | progress ] = list.add
+			promise[ tuple[1] ] = list.add;
+
+			// Handle state
+			if ( stateString ) {
+				list.add(function() {
+					// state = [ resolved | rejected ]
+					state = stateString;
+
+				// [ reject_list | resolve_list ].disable; progress_list.lock
+				}, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock );
+			}
+
+			// deferred[ resolve | reject | notify ]
+			deferred[ tuple[0] ] = function() {
+				deferred[ tuple[0] + "With" ]( this === deferred ? promise : this, arguments );
+				return this;
+			};
+			deferred[ tuple[0] + "With" ] = list.fireWith;
+		});
+
+		// Make the deferred a promise
+		promise.promise( deferred );
+
+		// Call given func if any
+		if ( func ) {
+			func.call( deferred, deferred );
+		}
+
+		// All done!
+		return deferred;
+	},
+
+	// Deferred helper
+	when: function( subordinate /* , ..., subordinateN */ ) {
+		var i = 0,
+			resolveValues = slice.call( arguments ),
+			length = resolveValues.length,
+
+			// the count of uncompleted subordinates
+			remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0,
+
+			// the master Deferred. If resolveValues consist of only a single Deferred, just use that.
+			deferred = remaining === 1 ? subordinate : jQuery.Deferred(),
+
+			// Update function for both resolve and progress values
+			updateFunc = function( i, contexts, values ) {
+				return function( value ) {
+					contexts[ i ] = this;
+					values[ i ] = arguments.length > 1 ? slice.call( arguments ) : value;
+					if ( values === progressValues ) {
+						deferred.notifyWith( contexts, values );
+					} else if ( !( --remaining ) ) {
+						deferred.resolveWith( contexts, values );
+					}
+				};
+			},
+
+			progressValues, progressContexts, resolveContexts;
+
+		// add listeners to Deferred subordinates; treat others as resolved
+		if ( length > 1 ) {
+			progressValues = new Array( length );
+			progressContexts = new Array( length );
+			resolveContexts = new Array( length );
+			for ( ; i < length; i++ ) {
+				if ( resolveValues[ i ] && jQuery.isFunction( resolveValues[ i ].promise ) ) {
+					resolveValues[ i ].promise()
+						.done( updateFunc( i, resolveContexts, resolveValues ) )
+						.fail( deferred.reject )
+						.progress( updateFunc( i, progressContexts, progressValues ) );
+				} else {
+					--remaining;
+				}
+			}
+		}
+
+		// if we're not waiting on anything, resolve the master
+		if ( !remaining ) {
+			deferred.resolveWith( resolveContexts, resolveValues );
+		}
+
+		return deferred.promise();
+	}
+});
+
+
+// The deferred used on DOM ready
+var readyList;
+
+jQuery.fn.ready = function( fn ) {
+	// Add the callback
+	jQuery.ready.promise().done( fn );
+
+	return this;
+};
+
+jQuery.extend({
+	// Is the DOM ready to be used? Set to true once it occurs.
+	isReady: false,
+
+	// A counter to track how many items to wait for before
+	// the ready event fires. See #6781
+	readyWait: 1,
+
+	// Hold (or release) the ready event
+	holdReady: function( hold ) {
+		if ( hold ) {
+			jQuery.readyWait++;
+		} else {
+			jQuery.ready( true );
+		}
+	},
+
+	// Handle when the DOM is ready
+	ready: function( wait ) {
+
+		// Abort if there are pending holds or we're already ready
+		if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) {
+			return;
+		}
+
+		// Remember that the DOM is ready
+		jQuery.isReady = true;
+
+		// If a normal DOM Ready event fired, decrement, and wait if need be
+		if ( wait !== true && --jQuery.readyWait > 0 ) {
+			return;
+		}
+
+		// If there are functions bound, to execute
+		readyList.resolveWith( document, [ jQuery ] );
+
+		// Trigger any bound ready events
+		if ( jQuery.fn.triggerHandler ) {
+			jQuery( document ).triggerHandler( "ready" );
+			jQuery( document ).off( "ready" );
+		}
+	}
+});
+
+/**
+ * The ready event handler and self cleanup method
+ */
+function completed() {
+	document.removeEventListener( "DOMContentLoaded", completed, false );
+	window.removeEventListener( "load", completed, false );
+	jQuery.ready();
+}
+
+jQuery.ready.promise = function( obj ) {
+	if ( !readyList ) {
+
+		readyList = jQuery.Deferred();
+
+		// Catch cases where $(document).ready() is called after the browser event has already occurred.
+		// we once tried to use readyState "interactive" here, but it caused issues like the one
+		// discovered by ChrisS here: http://bugs.jquery.com/ticket/12282#comment:15
+		if ( document.readyState === "complete" ) {
+			// Handle it asynchronously to allow scripts the opportunity to delay ready
+			setTimeout( jQuery.ready );
+
+		} else {
+
+			// Use the handy event callback
+			document.addEventListener( "DOMContentLoaded", completed, false );
+
+			// A fallback to window.onload, that will always work
+			window.addEventListener( "load", completed, false );
+		}
+	}
+	return readyList.promise( obj );
+};
+
+// Kick off the DOM ready check even if the user does not
+jQuery.ready.promise();
+
+
+
+
+// Multifunctional method to get and set values of a collection
+// The value/s can optionally be executed if it's a function
+var access = jQuery.access = function( elems, fn, key, value, chainable, emptyGet, raw ) {
+	var i = 0,
+		len = elems.length,
+		bulk = key == null;
+
+	// Sets many values
+	if ( jQuery.type( key ) === "object" ) {
+		chainable = true;
+		for ( i in key ) {
+			jQuery.access( elems, fn, i, key[i], true, emptyGet, raw );
+		}
+
+	// Sets one value
+	} else if ( value !== undefined ) {
+		chainable = true;
+
+		if ( !jQuery.isFunction( value ) ) {
+			raw = true;
+		}
+
+		if ( bulk ) {
+			// Bulk operations run against the entire set
+			if ( raw ) {
+				fn.call( elems, value );
+				fn = null;
+
+			// ...except when executing function values
+			} else {
+				bulk = fn;
+				fn = function( elem, key, value ) {
+					return bulk.call( jQuery( elem ), value );
+				};
+			}
+		}
+
+		if ( fn ) {
+			for ( ; i < len; i++ ) {
+				fn( elems[i], key, raw ? value : value.call( elems[i], i, fn( elems[i], key ) ) );
+			}
+		}
+	}
+
+	return chainable ?
+		elems :
+
+		// Gets
+		bulk ?
+			fn.call( elems ) :
+			len ? fn( elems[0], key ) : emptyGet;
+};
+
+
+/**
+ * Determines whether an object can have data
+ */
+jQuery.acceptData = function( owner ) {
+	// Accepts only:
+	//  - Node
+	//    - Node.ELEMENT_NODE
+	//    - Node.DOCUMENT_NODE
+	//  - Object
+	//    - Any
+	/* jshint -W018 */
+	return owner.nodeType === 1 || owner.nodeType === 9 || !( +owner.nodeType );
+};
+
+
+function Data() {
+	// Support: Android < 4,
+	// Old WebKit does not have Object.preventExtensions/freeze method,
+	// return new empty object instead with no [[set]] accessor
+	Object.defineProperty( this.cache = {}, 0, {
+		get: function() {
+			return {};
+		}
+	});
+
+	this.expando = jQuery.expando + Math.random();
+}
+
+Data.uid = 1;
+Data.accepts = jQuery.acceptData;
+
+Data.prototype = {
+	key: function( owner ) {
+		// We can accept data for non-element nodes in modern browsers,
+		// but we should not, see #8335.
+		// Always return the key for a frozen object.
+		if ( !Data.accepts( owner ) ) {
+			return 0;
+		}
+
+		var descriptor = {},
+			// Check if the owner object already has a cache key
+			unlock = owner[ this.expando ];
+
+		// If not, create one
+		if ( !unlock ) {
+			unlock = Data.uid++;
+
+			// Secure it in a non-enumerable, non-writable property
+			try {
+				descriptor[ this.expando ] = { value: unlock };
+				Object.defineProperties( owner, descriptor );
+
+			// Support: Android < 4
+			// Fallback to a less secure definition
+			} catch ( e ) {
+				descriptor[ this.expando ] = unlock;
+				jQuery.extend( owner, descriptor );
+			}
+		}
+
+		// Ensure the cache object
+		if ( !this.cache[ unlock ] ) {
+			this.cache[ unlock ] = {};
+		}
+
+		return unlock;
+	},
+	set: function( owner, data, value ) {
+		var prop,
+			// There may be an unlock assigned to this node,
+			// if there is no entry for this "owner", create one inline
+			// and set the unlock as though an owner entry had always existed
+			unlock = this.key( owner ),
+			cache = this.cache[ unlock ];
+
+		// Handle: [ owner, key, value ] args
+		if ( typeof data === "string" ) {
+			cache[ data ] = value;
+
+		// Handle: [ owner, { properties } ] args
+		} else {
+			// Fresh assignments by object are shallow copied
+			if ( jQuery.isEmptyObject( cache ) ) {
+				jQuery.extend( this.cache[ unlock ], data );
+			// Otherwise, copy the properties one-by-one to the cache object
+			} else {
+				for ( prop in data ) {
+					cache[ prop ] = data[ prop ];
+				}
+			}
+		}
+		return cache;
+	},
+	get: function( owner, key ) {
+		// Either a valid cache is found, or will be created.
+		// New caches will be created and the unlock returned,
+		// allowing direct access to the newly created
+		// empty data object. A valid owner object must be provided.
+		var cache = this.cache[ this.key( owner ) ];
+
+		return key === undefined ?
+			cache : cache[ key ];
+	},
+	access: function( owner, key, value ) {
+		var stored;
+		// In cases where either:
+		//
+		//   1. No key was specified
+		//   2. A string key was specified, but no value provided
+		//
+		// Take the "read" path and allow the get method to determine
+		// which value to return, respectively either:
+		//
+		//   1. The entire cache object
+		//   2. The data stored at the key
+		//
+		if ( key === undefined ||
+				((key && typeof key === "string") && value === undefined) ) {
+
+			stored = this.get( owner, key );
+
+			return stored !== undefined ?
+				stored : this.get( owner, jQuery.camelCase(key) );
+		}
+
+		// [*]When the key is not a string, or both a key and value
+		// are specified, set or extend (existing objects) with either:
+		//
+		//   1. An object of properties
+		//   2. A key and value
+		//
+		this.set( owner, key, value );
+
+		// Since the "set" path can have two possible entry points
+		// return the expected data based on which path was taken[*]
+		return value !== undefined ? value : key;
+	},
+	remove: function( owner, key ) {
+		var i, name, camel,
+			unlock = this.key( owner ),
+			cache = this.cache[ unlock ];
+
+		if ( key === undefined ) {
+			this.cache[ unlock ] = {};
+
+		} else {
+			// Support array or space separated string of keys
+			if ( jQuery.isArray( key ) ) {
+				// If "name" is an array of keys...
+				// When data is initially created, via ("key", "val") signature,
+				// keys will be converted to camelCase.
+				// Since there is no way to tell _how_ a key was added, remove
+				// both plain key and camelCase key. #12786
+				// This will only penalize the array argument path.
+				name = key.concat( key.map( jQuery.camelCase ) );
+			} else {
+				camel = jQuery.camelCase( key );
+				// Try the string as a key before any manipulation
+				if ( key in cache ) {
+					name = [ key, camel ];
+				} else {
+					// If a key with the spaces exists, use it.
+					// Otherwise, create an array by matching non-whitespace
+					name = camel;
+					name = name in cache ?
+						[ name ] : ( name.match( rnotwhite ) || [] );
+				}
+			}
+
+			i = name.length;
+			while ( i-- ) {
+				delete cache[ name[ i ] ];
+			}
+		}
+	},
+	hasData: function( owner ) {
+		return !jQuery.isEmptyObject(
+			this.cache[ owner[ this.expando ] ] || {}
+		);
+	},
+	discard: function( owner ) {
+		if ( owner[ this.expando ] ) {
+			delete this.cache[ owner[ this.expando ] ];
+		}
+	}
+};
+var data_priv = new Data();
+
+var data_user = new Data();
+
+
+
+/*
+	Implementation Summary
+
+	1. Enforce API surface and semantic compatibility with 1.9.x branch
+	2. Improve the module's maintainability by reducing the storage
+		paths to a single mechanism.
+	3. Use the same single mechanism to support "private" and "user" data.
+	4. _Never_ expose "private" data to user code (TODO: Drop _data, _removeData)
+	5. Avoid exposing implementation details on user objects (eg. expando properties)
+	6. Provide a clear path for implementation upgrade to WeakMap in 2014
+*/
+var rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,
+	rmultiDash = /([A-Z])/g;
+
+function dataAttr( elem, key, data ) {
+	var name;
+
+	// If nothing was found internally, try to fetch any
+	// data from the HTML5 data-* attribute
+	if ( data === undefined && elem.nodeType === 1 ) {
+		name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase();
+		data = elem.getAttribute( name );
+
+		if ( typeof data === "string" ) {
+			try {
+				data = data === "true" ? true :
+					data === "false" ? false :
+					data === "null" ? null :
+					// Only convert to a number if it doesn't change the string
+					+data + "" === data ? +data :
+					rbrace.test( data ) ? jQuery.parseJSON( data ) :
+					data;
+			} catch( e ) {}
+
+			// Make sure we set the data so it isn't changed later
+			data_user.set( elem, key, data );
+		} else {
+			data = undefined;
+		}
+	}
+	return data;
+}
+
+jQuery.extend({
+	hasData: function( elem ) {
+		return data_user.hasData( elem ) || data_priv.hasData( elem );
+	},
+
+	data: function( elem, name, data ) {
+		return data_user.access( elem, name, data );
+	},
+
+	removeData: function( elem, name ) {
+		data_user.remove( elem, name );
+	},
+
+	// TODO: Now that all calls to _data and _removeData have been replaced
+	// with direct calls to data_priv methods, these can be deprecated.
+	_data: function( elem, name, data ) {
+		return data_priv.access( elem, name, data );
+	},
+
+	_removeData: function( elem, name ) {
+		data_priv.remove( elem, name );
+	}
+});
+
+jQuery.fn.extend({
+	data: function( key, value ) {
+		var i, name, data,
+			elem = this[ 0 ],
+			attrs = elem && elem.attributes;
+
+		// Gets all values
+		if ( key === undefined ) {
+			if ( this.length ) {
+				data = data_user.get( elem );
+
+				if ( elem.nodeType === 1 && !data_priv.get( elem, "hasDataAttrs" ) ) {
+					i = attrs.length;
+					while ( i-- ) {
+
+						// Support: IE11+
+						// The attrs elements can be null (#14894)
+						if ( attrs[ i ] ) {
+							name = attrs[ i ].name;
+							if ( name.indexOf( "data-" ) === 0 ) {
+								name = jQuery.camelCase( name.slice(5) );
+								dataAttr( elem, name, data[ name ] );
+							}
+						}
+					}
+					data_priv.set( elem, "hasDataAttrs", true );
+				}
+			}
+
+			return data;
+		}
+
+		// Sets multiple values
+		if ( typeof key === "object" ) {
+			return this.each(function() {
+				data_user.set( this, key );
+			});
+		}
+
+		return access( this, function( value ) {
+			var data,
+				camelKey = jQuery.camelCase( key );
+
+			// The calling jQuery object (element matches) is not empty
+			// (and therefore has an element appears at this[ 0 ]) and the
+			// `value` parameter was not undefined. An empty jQuery object
+			// will result in `undefined` for elem = this[ 0 ] which will
+			// throw an exception if an attempt to read a data cache is made.
+			if ( elem && value === undefined ) {
+				// Attempt to get data from the cache
+				// with the key as-is
+				data = data_user.get( elem, key );
+				if ( data !== undefined ) {
+					return data;
+				}
+
+				// Attempt to get data from the cache
+				// with the key camelized
+				data = data_user.get( elem, camelKey );
+				if ( data !== undefined ) {
+					return data;
+				}
+
+				// Attempt to "discover" the data in
+				// HTML5 custom data-* attrs
+				data = dataAttr( elem, camelKey, undefined );
+				if ( data !== undefined ) {
+					return data;
+				}
+
+				// We tried really hard, but the data doesn't exist.
+				return;
+			}
+
+			// Set the data...
+			this.each(function() {
+				// First, attempt to store a copy or reference of any
+				// data that might've been store with a camelCased key.
+				var data = data_user.get( this, camelKey );
+
+				// For HTML5 data-* attribute interop, we have to
+				// store property names with dashes in a camelCase form.
+				// This might not apply to all properties...*
+				data_user.set( this, camelKey, value );
+
+				// *... In the case of properties that might _actually_
+				// have dashes, we need to also store a copy of that
+				// unchanged property.
+				if ( key.indexOf("-") !== -1 && data !== undefined ) {
+					data_user.set( this, key, value );
+				}
+			});
+		}, null, value, arguments.length > 1, null, true );
+	},
+
+	removeData: function( key ) {
+		return this.each(function() {
+			data_user.remove( this, key );
+		});
+	}
+});
+
+
+jQuery.extend({
+	queue: function( elem, type, data ) {
+		var queue;
+
+		if ( elem ) {
+			type = ( type || "fx" ) + "queue";
+			queue = data_priv.get( elem, type );
+
+			// Speed up dequeue by getting out quickly if this is just a lookup
+			if ( data ) {
+				if ( !queue || jQuery.isArray( data ) ) {
+					queue = data_priv.access( elem, type, jQuery.makeArray(data) );
+				} else {
+					queue.push( data );
+				}
+			}
+			return queue || [];
+		}
+	},
+
+	dequeue: function( elem, type ) {
+		type = type || "fx";
+
+		var queue = jQuery.queue( elem, type ),
+			startLength = queue.length,
+			fn = queue.shift(),
+			hooks = jQuery._queueHooks( elem, type ),
+			next = function() {
+				jQuery.dequeue( elem, type );
+			};
+
+		// If the fx queue is dequeued, always remove the progress sentinel
+		if ( fn === "inprogress" ) {
+			fn = queue.shift();
+			startLength--;
+		}
+
+		if ( fn ) {
+
+			// Add a progress sentinel to prevent the fx queue from being
+			// automatically dequeued
+			if ( type === "fx" ) {
+				queue.unshift( "inprogress" );
+			}
+
+			// clear up the last queue stop function
+			delete hooks.stop;
+			fn.call( elem, next, hooks );
+		}
+
+		if ( !startLength && hooks ) {
+			hooks.empty.fire();
+		}
+	},
+
+	// not intended for public consumption - generates a queueHooks object, or returns the current one
+	_queueHooks: function( elem, type ) {
+		var key = type + "queueHooks";
+		return data_priv.get( elem, key ) || data_priv.access( elem, key, {
+			empty: jQuery.Callbacks("once memory").add(function() {
+				data_priv.remove( elem, [ type + "queue", key ] );
+			})
+		});
+	}
+});
+
+jQuery.fn.extend({
+	queue: function( type, data ) {
+		var setter = 2;
+
+		if ( typeof type !== "string" ) {
+			data = type;
+			type = "fx";
+			setter--;
+		}
+
+		if ( arguments.length < setter ) {
+			return jQuery.queue( this[0], type );
+		}
+
+		return data === undefined ?
+			this :
+			this.each(function() {
+				var queue = jQuery.queue( this, type, data );
+
+				// ensure a hooks for this queue
+				jQuery._queueHooks( this, type );
+
+				if ( type === "fx" && queue[0] !== "inprogress" ) {
+					jQuery.dequeue( this, type );
+				}
+			});
+	},
+	dequeue: function( type ) {
+		return this.each(function() {
+			jQuery.dequeue( this, type );
+		});
+	},
+	clearQueue: function( type ) {
+		return this.queue( type || "fx", [] );
+	},
+	// Get a promise resolved when queues of a certain type
+	// are emptied (fx is the type by default)
+	promise: function( type, obj ) {
+		var tmp,
+			count = 1,
+			defer = jQuery.Deferred(),
+			elements = this,
+			i = this.length,
+			resolve = function() {
+				if ( !( --count ) ) {
+					defer.resolveWith( elements, [ elements ] );
+				}
+			};
+
+		if ( typeof type !== "string" ) {
+			obj = type;
+			type = undefined;
+		}
+		type = type || "fx";
+
+		while ( i-- ) {
+			tmp = data_priv.get( elements[ i ], type + "queueHooks" );
+			if ( tmp && tmp.empty ) {
+				count++;
+				tmp.empty.add( resolve );
+			}
+		}
+		resolve();
+		return defer.promise( obj );
+	}
+});
+var pnum = (/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/).source;
+
+var cssExpand = [ "Top", "Right", "Bottom", "Left" ];
+
+var isHidden = function( elem, el ) {
+		// isHidden might be called from jQuery#filter function;
+		// in that case, element will be second argument
+		elem = el || elem;
+		return jQuery.css( elem, "display" ) === "none" || !jQuery.contains( elem.ownerDocument, elem );
+	};
+
+var rcheckableType = (/^(?:checkbox|radio)$/i);
+
+
+
+(function() {
+	var fragment = document.createDocumentFragment(),
+		div = fragment.appendChild( document.createElement( "div" ) ),
+		input = document.createElement( "input" );
+
+	// #11217 - WebKit loses check when the name is after the checked attribute
+	// Support: Windows Web Apps (WWA)
+	// `name` and `type` need .setAttribute for WWA
+	input.setAttribute( "type", "radio" );
+	input.setAttribute( "checked", "checked" );
+	input.setAttribute( "name", "t" );
+
+	div.appendChild( input );
+
+	// Support: Safari 5.1, iOS 5.1, Android 4.x, Android 2.3
+	// old WebKit doesn't clone checked state correctly in fragments
+	support.checkClone = div.cloneNode( true ).cloneNode( true ).lastChild.checked;
+
+	// Make sure textarea (and checkbox) defaultValue is properly cloned
+	// Support: IE9-IE11+
+	div.innerHTML = "<textarea>x</textarea>";
+	support.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue;
+})();
+var strundefined = typeof undefined;
+
+
+
+support.focusinBubbles = "onfocusin" in window;
+
+
+var
+	rkeyEvent = /^key/,
+	rmouseEvent = /^(?:mouse|pointer|contextmenu)|click/,
+	rfocusMorph = /^(?:focusinfocus|focusoutblur)$/,
+	rtypenamespace = /^([^.]*)(?:\.(.+)|)$/;
+
+function returnTrue() {
+	return true;
+}
+
+function returnFalse() {
+	return false;
+}
+
+function safeActiveElement() {
+	try {
+		return document.activeElement;
+	} catch ( err ) { }
+}
+
+/*
+ * Helper functions for managing events -- not part of the public interface.
+ * Props to Dean Edwards' addEvent library for many of the ideas.
+ */
+jQuery.event = {
+
+	global: {},
+
+	add: function( elem, types, handler, data, selector ) {
+
+		var handleObjIn, eventHandle, tmp,
+			events, t, handleObj,
+			special, handlers, type, namespaces, origType,
+			elemData = data_priv.get( elem );
+
+		// Don't attach events to noData or text/comment nodes (but allow plain objects)
+		if ( !elemData ) {
+			return;
+		}
+
+		// Caller can pass in an object of custom data in lieu of the handler
+		if ( handler.handler ) {
+			handleObjIn = handler;
+			handler = handleObjIn.handler;
+			selector = handleObjIn.selector;
+		}
+
+		// Make sure that the handler has a unique ID, used to find/remove it later
+		if ( !handler.guid ) {
+			handler.guid = jQuery.guid++;
+		}
+
+		// Init the element's event structure and main handler, if this is the first
+		if ( !(events = elemData.events) ) {
+			events = elemData.events = {};
+		}
+		if ( !(eventHandle = elemData.handle) ) {
+			eventHandle = elemData.handle = function( e ) {
+				// Discard the second event of a jQuery.event.trigger() and
+				// when an event is called after a page has unloaded
+				return typeof jQuery !== strundefined && jQuery.event.triggered !== e.type ?
+					jQuery.event.dispatch.apply( elem, arguments ) : undefined;
+			};
+		}
+
+		// Handle multiple events separated by a space
+		types = ( types || "" ).match( rnotwhite ) || [ "" ];
+		t = types.length;
+		while ( t-- ) {
+			tmp = rtypenamespace.exec( types[t] ) || [];
+			type = origType = tmp[1];
+			namespaces = ( tmp[2] || "" ).split( "." ).sort();
+
+			// There *must* be a type, no attaching namespace-only handlers
+			if ( !type ) {
+				continue;
+			}
+
+			// If event changes its type, use the special event handlers for the changed type
+			special = jQuery.event.special[ type ] || {};
+
+			// If selector defined, determine special event api type, otherwise given type
+			type = ( selector ? special.delegateType : special.bindType ) || type;
+
+			// Update special based on newly reset type
+			special = jQuery.event.special[ type ] || {};
+
+			// handleObj is passed to all event handlers
+			handleObj = jQuery.extend({
+				type: type,
+				origType: origType,
+				data: data,
+				handler: handler,
+				guid: handler.guid,
+				selector: selector,
+				needsContext: selector && jQuery.expr.match.needsContext.test( selector ),
+				namespace: namespaces.join(".")
+			}, handleObjIn );
+
+			// Init the event handler queue if we're the first
+			if ( !(handlers = events[ type ]) ) {
+				handlers = events[ type ] = [];
+				handlers.delegateCount = 0;
+
+				// Only use addEventListener if the special events handler returns false
+				if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) {
+					if ( elem.addEventListener ) {
+						elem.addEventListener( type, eventHandle, false );
+					}
+				}
+			}
+
+			if ( special.add ) {
+				special.add.call( elem, handleObj );
+
+				if ( !handleObj.handler.guid ) {
+					handleObj.handler.guid = handler.guid;
+				}
+			}
+
+			// Add to the element's handler list, delegates in front
+			if ( selector ) {
+				handlers.splice( handlers.delegateCount++, 0, handleObj );
+			} else {
+				handlers.push( handleObj );
+			}
+
+			// Keep track of which events have ever been used, for event optimization
+			jQuery.event.global[ type ] = true;
+		}
+
+	},
+
+	// Detach an event or set of events from an element
+	remove: function( elem, types, handler, selector, mappedTypes ) {
+
+		var j, origCount, tmp,
+			events, t, handleObj,
+			special, handlers, type, namespaces, origType,
+			elemData = data_priv.hasData( elem ) && data_priv.get( elem );
+
+		if ( !elemData || !(events = elemData.events) ) {
+			return;
+		}
+
+		// Once for each type.namespace in types; type may be omitted
+		types = ( types || "" ).match( rnotwhite ) || [ "" ];
+		t = types.length;
+		while ( t-- ) {
+			tmp = rtypenamespace.exec( types[t] ) || [];
+			type = origType = tmp[1];
+			namespaces = ( tmp[2] || "" ).split( "." ).sort();
+
+			// Unbind all events (on this namespace, if provided) for the element
+			if ( !type ) {
+				for ( type in events ) {
+					jQuery.event.remove( elem, type + types[ t ], handler, selector, true );
+				}
+				continue;
+			}
+
+			special = jQuery.event.special[ type ] || {};
+			type = ( selector ? special.delegateType : special.bindType ) || type;
+			handlers = events[ type ] || [];
+			tmp = tmp[2] && new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" );
+
+			// Remove matching events
+			origCount = j = handlers.length;
+			while ( j-- ) {
+				handleObj = handlers[ j ];
+
+				if ( ( mappedTypes || origType === handleObj.origType ) &&
+					( !handler || handler.guid === handleObj.guid ) &&
+					( !tmp || tmp.test( handleObj.namespace ) ) &&
+					( !selector || selector === handleObj.selector || selector === "**" && handleObj.selector ) ) {
+					handlers.splice( j, 1 );
+
+					if ( handleObj.selector ) {
+						handlers.delegateCount--;
+					}
+					if ( special.remove ) {
+						special.remove.call( elem, handleObj );
+					}
+				}
+			}
+
+			// Remove generic event handler if we removed something and no more handlers exist
+			// (avoids potential for endless recursion during removal of special event handlers)
+			if ( origCount && !handlers.length ) {
+				if ( !special.teardown || special.teardown.call( elem, namespaces, elemData.handle ) === false ) {
+					jQuery.removeEvent( elem, type, elemData.handle );
+				}
+
+				delete events[ type ];
+			}
+		}
+
+		// Remove the expando if it's no longer used
+		if ( jQuery.isEmptyObject( events ) ) {
+			delete elemData.handle;
+			data_priv.remove( elem, "events" );
+		}
+	},
+
+	trigger: function( event, data, elem, onlyHandlers ) {
+
+		var i, cur, tmp, bubbleType, ontype, handle, special,
+			eventPath = [ elem || document ],
+			type = hasOwn.call( event, "type" ) ? event.type : event,
+			namespaces = hasOwn.call( event, "namespace" ) ? event.namespace.split(".") : [];
+
+		cur = tmp = elem = elem || document;
+
+		// Don't do events on text and comment nodes
+		if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
+			return;
+		}
+
+		// focus/blur morphs to focusin/out; ensure we're not firing them right now
+		if ( rfocusMorph.test( type + jQuery.event.triggered ) ) {
+			return;
+		}
+
+		if ( type.indexOf(".") >= 0 ) {
+			// Namespaced trigger; create a regexp to match event type in handle()
+			namespaces = type.split(".");
+			type = namespaces.shift();
+			namespaces.sort();
+		}
+		ontype = type.indexOf(":") < 0 && "on" + type;
+
+		// Caller can pass in a jQuery.Event object, Object, or just an event type string
+		event = event[ jQuery.expando ] ?
+			event :
+			new jQuery.Event( type, typeof event === "object" && event );
+
+		// Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true)
+		event.isTrigger = onlyHandlers ? 2 : 3;
+		event.namespace = namespaces.join(".");
+		event.namespace_re = event.namespace ?
+			new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" ) :
+			null;
+
+		// Clean up the event in case it is being reused
+		event.result = undefined;
+		if ( !event.target ) {
+			event.target = elem;
+		}
+
+		// Clone any incoming data and prepend the event, creating the handler arg list
+		data = data == null ?
+			[ event ] :
+			jQuery.makeArray( data, [ event ] );
+
+		// Allow special events to draw outside the lines
+		special = jQuery.event.special[ type ] || {};
+		if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) {
+			return;
+		}
+
+		// Determine event propagation path in advance, per W3C events spec (#9951)
+		// Bubble up to document, then to window; watch for a global ownerDocument var (#9724)
+		if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) {
+
+			bubbleType = special.delegateType || type;
+			if ( !rfocusMorph.test( bubbleType + type ) ) {
+				cur = cur.parentNode;
+			}
+			for ( ; cur; cur = cur.parentNode ) {
+				eventPath.push( cur );
+				tmp = cur;
+			}
+
+			// Only add window if we got to document (e.g., not plain obj or detached DOM)
+			if ( tmp === (elem.ownerDocument || document) ) {
+				eventPath.push( tmp.defaultView || tmp.parentWindow || window );
+			}
+		}
+
+		// Fire handlers on the event path
+		i = 0;
+		while ( (cur = eventPath[i++]) && !event.isPropagationStopped() ) {
+
+			event.type = i > 1 ?
+				bubbleType :
+				special.bindType || type;
+
+			// jQuery handler
+			handle = ( data_priv.get( cur, "events" ) || {} )[ event.type ] && data_priv.get( cur, "handle" );
+			if ( handle ) {
+				handle.apply( cur, data );
+			}
+
+			// Native handler
+			handle = ontype && cur[ ontype ];
+			if ( handle && handle.apply && jQuery.acceptData( cur ) ) {
+				event.result = handle.apply( cur, data );
+				if ( event.result === false ) {
+					event.preventDefault();
+				}
+			}
+		}
+		event.type = type;
+
+		// If nobody prevented the default action, do it now
+		if ( !onlyHandlers && !event.isDefaultPrevented() ) {
+
+			if ( (!special._default || special._default.apply( eventPath.pop(), data ) === false) &&
+				jQuery.acceptData( elem ) ) {
+
+				// Call a native DOM method on the target with the same name name as the event.
+				// Don't do default actions on window, that's where global variables be (#6170)
+				if ( ontype && jQuery.isFunction( elem[ type ] ) && !jQuery.isWindow( elem ) ) {
+
+					// Don't re-trigger an onFOO event when we call its FOO() method
+					tmp = elem[ ontype ];
+
+					if ( tmp ) {
+						elem[ ontype ] = null;
+					}
+
+					// Prevent re-triggering of the same event, since we already bubbled it above
+					jQuery.event.triggered = type;
+					elem[ type ]();
+					jQuery.event.triggered = undefined;
+
+					if ( tmp ) {
+						elem[ ontype ] = tmp;
+					}
+				}
+			}
+		}
+
+		return event.result;
+	},
+
+	dispatch: function( event ) {
+
+		// Make a writable jQuery.Event from the native event object
+		event = jQuery.event.fix( event );
+
+		var i, j, ret, matched, handleObj,
+			handlerQueue = [],
+			args = slice.call( arguments ),
+			handlers = ( data_priv.get( this, "events" ) || {} )[ event.type ] || [],
+			special = jQuery.event.special[ event.type ] || {};
+
+		// Use the fix-ed jQuery.Event rather than the (read-only) native event
+		args[0] = event;
+		event.delegateTarget = this;
+
+		// Call the preDispatch hook for the mapped type, and let it bail if desired
+		if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) {
+			return;
+		}
+
+		// Determine handlers
+		handlerQueue = jQuery.event.handlers.call( this, event, handlers );
+
+		// Run delegates first; they may want to stop propagation beneath us
+		i = 0;
+		while ( (matched = handlerQueue[ i++ ]) && !event.isPropagationStopped() ) {
+			event.currentTarget = matched.elem;
+
+			j = 0;
+			while ( (handleObj = matched.handlers[ j++ ]) && !event.isImmediatePropagationStopped() ) {
+
+				// Triggered event must either 1) have no namespace, or
+				// 2) have namespace(s) a subset or equal to those in the bound event (both can have no namespace).
+				if ( !event.namespace_re || event.namespace_re.test( handleObj.namespace ) ) {
+
+					event.handleObj = handleObj;
+					event.data = handleObj.data;
+
+					ret = ( (jQuery.event.special[ handleObj.origType ] || {}).handle || handleObj.handler )
+							.apply( matched.elem, args );
+
+					if ( ret !== undefined ) {
+						if ( (event.result = ret) === false ) {
+							event.preventDefault();
+							event.stopPropagation();
+						}
+					}
+				}
+			}
+		}
+
+		// Call the postDispatch hook for the mapped type
+		if ( special.postDispatch ) {
+			special.postDispatch.call( this, event );
+		}
+
+		return event.result;
+	},
+
+	handlers: function( event, handlers ) {
+		var i, matches, sel, handleObj,
+			handlerQueue = [],
+			delegateCount = handlers.delegateCount,
+			cur = event.target;
+
+		// Find delegate handlers
+		// Black-hole SVG <use> instance trees (#13180)
+		// Avoid non-left-click bubbling in Firefox (#3861)
+		if ( delegateCount && cur.nodeType && (!event.button || event.type !== "click") ) {
+
+			for ( ; cur !== this; cur = cur.parentNode || this ) {
+
+				// Don't process clicks on disabled elements (#6911, #8165, #11382, #11764)
+				if ( cur.disabled !== true || event.type !== "click" ) {
+					matches = [];
+					for ( i = 0; i < delegateCount; i++ ) {
+						handleObj = handlers[ i ];
+
+						// Don't conflict with Object.prototype properties (#13203)
+						sel = handleObj.selector + " ";
+
+						if ( matches[ sel ] === undefined ) {
+							matches[ sel ] = handleObj.needsContext ?
+								jQuery( sel, this ).index( cur ) >= 0 :
+								jQuery.find( sel, this, null, [ cur ] ).length;
+						}
+						if ( matches[ sel ] ) {
+							matches.push( handleObj );
+						}
+					}
+					if ( matches.length ) {
+						handlerQueue.push({ elem: cur, handlers: matches });
+					}
+				}
+			}
+		}
+
+		// Add the remaining (directly-bound) handlers
+		if ( delegateCount < handlers.length ) {
+			handlerQueue.push({ elem: this, handlers: handlers.slice( delegateCount ) });
+		}
+
+		return handlerQueue;
+	},
+
+	// Includes some event props shared by KeyEvent and MouseEvent
+	props: "altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),
+
+	fixHooks: {},
+
+	keyHooks: {
+		props: "char charCode key keyCode".split(" "),
+		filter: function( event, original ) {
+
+			// Add which for key events
+			if ( event.which == null ) {
+				event.which = original.charCode != null ? original.charCode : original.keyCode;
+			}
+
+			return event;
+		}
+	},
+
+	mouseHooks: {
+		props: "button buttons clientX clientY offsetX offsetY pageX pageY screenX screenY toElement".split(" "),
+		filter: function( event, original ) {
+			var eventDoc, doc, body,
+				button = original.button;
+
+			// Calculate pageX/Y if missing and clientX/Y available
+			if ( event.pageX == null && original.clientX != null ) {
+				eventDoc = event.target.ownerDocument || document;
+				doc = eventDoc.documentElement;
+				body = eventDoc.body;
+
+				event.pageX = original.clientX + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - ( doc && doc.clientLeft || body && body.clientLeft || 0 );
+				event.pageY = original.clientY + ( doc && doc.scrollTop  || body && body.scrollTop  || 0 ) - ( doc && doc.clientTop  || body && body.clientTop  || 0 );
+			}
+
+			// Add which for click: 1 === left; 2 === middle; 3 === right
+			// Note: button is not normalized, so don't use it
+			if ( !event.which && button !== undefined ) {
+				event.which = ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) );
+			}
+
+			return event;
+		}
+	},
+
+	fix: function( event ) {
+		if ( event[ jQuery.expando ] ) {
+			return event;
+		}
+
+		// Create a writable copy of the event object and normalize some properties
+		var i, prop, copy,
+			type = event.type,
+			originalEvent = event,
+			fixHook = this.fixHooks[ type ];
+
+		if ( !fixHook ) {
+			this.fixHooks[ type ] = fixHook =
+				rmouseEvent.test( type ) ? this.mouseHooks :
+				rkeyEvent.test( type ) ? this.keyHooks :
+				{};
+		}
+		copy = fixHook.props ? this.props.concat( fixHook.props ) : this.props;
+
+		event = new jQuery.Event( originalEvent );
+
+		i = copy.length;
+		while ( i-- ) {
+			prop = copy[ i ];
+			event[ prop ] = originalEvent[ prop ];
+		}
+
+		// Support: Cordova 2.5 (WebKit) (#13255)
+		// All events should have a target; Cordova deviceready doesn't
+		if ( !event.target ) {
+			event.target = document;
+		}
+
+		// Support: Safari 6.0+, Chrome < 28
+		// Target should not be a text node (#504, #13143)
+		if ( event.target.nodeType === 3 ) {
+			event.target = event.target.parentNode;
+		}
+
+		return fixHook.filter ? fixHook.filter( event, originalEvent ) : event;
+	},
+
+	special: {
+		load: {
+			// Prevent triggered image.load events from bubbling to window.load
+			noBubble: true
+		},
+		focus: {
+			// Fire native event if possible so blur/focus sequence is correct
+			trigger: function() {
+				if ( this !== safeActiveElement() && this.focus ) {
+					this.focus();
+					return false;
+				}
+			},
+			delegateType: "focusin"
+		},
+		blur: {
+			trigger: function() {
+				if ( this === safeActiveElement() && this.blur ) {
+					this.blur();
+					return false;
+				}
+			},
+			delegateType: "focusout"
+		},
+		click: {
+			// For checkbox, fire native event so checked state will be right
+			trigger: function() {
+				if ( this.type === "checkbox" && this.click && jQuery.nodeName( this, "input" ) ) {
+					this.click();
+					return false;
+				}
+			},
+
+			// For cross-browser consistency, don't fire native .click() on links
+			_default: function( event ) {
+				return jQuery.nodeName( event.target, "a" );
+			}
+		},
+
+		beforeunload: {
+			postDispatch: function( event ) {
+
+				// Support: Firefox 20+
+				// Firefox doesn't alert if the returnValue field is not set.
+				if ( event.result !== undefined && event.originalEvent ) {
+					event.originalEvent.returnValue = event.result;
+				}
+			}
+		}
+	},
+
+	simulate: function( type, elem, event, bubble ) {
+		// Piggyback on a donor event to simulate a different one.
+		// Fake originalEvent to avoid donor's stopPropagation, but if the
+		// simulated event prevents default then we do the same on the donor.
+		var e = jQuery.extend(
+			new jQuery.Event(),
+			event,
+			{
+				type: type,
+				isSimulated: true,
+				originalEvent: {}
+			}
+		);
+		if ( bubble ) {
+			jQuery.event.trigger( e, null, elem );
+		} else {
+			jQuery.event.dispatch.call( elem, e );
+		}
+		if ( e.isDefaultPrevented() ) {
+			event.preventDefault();
+		}
+	}
+};
+
+jQuery.removeEvent = function( elem, type, handle ) {
+	if ( elem.removeEventListener ) {
+		elem.removeEventListener( type, handle, false );
+	}
+};
+
+jQuery.Event = function( src, props ) {
+	// Allow instantiation without the 'new' keyword
+	if ( !(this instanceof jQuery.Event) ) {
+		return new jQuery.Event( src, props );
+	}
+
+	// Event object
+	if ( src && src.type ) {
+		this.originalEvent = src;
+		this.type = src.type;
+
+		// Events bubbling up the document may have been marked as prevented
+		// by a handler lower down the tree; reflect the correct value.
+		this.isDefaultPrevented = src.defaultPrevented ||
+				src.defaultPrevented === undefined &&
+				// Support: Android < 4.0
+				src.returnValue === false ?
+			returnTrue :
+			returnFalse;
+
+	// Event type
+	} else {
+		this.type = src;
+	}
+
+	// Put explicitly provided properties onto the event object
+	if ( props ) {
+		jQuery.extend( this, props );
+	}
+
+	// Create a timestamp if incoming event doesn't have one
+	this.timeStamp = src && src.timeStamp || jQuery.now();
+
+	// Mark it as fixed
+	this[ jQuery.expando ] = true;
+};
+
+// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding
+// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
+jQuery.Event.prototype = {
+	isDefaultPrevented: returnFalse,
+	isPropagationStopped: returnFalse,
+	isImmediatePropagationStopped: returnFalse,
+
+	preventDefault: function() {
+		var e = this.originalEvent;
+
+		this.isDefaultPrevented = returnTrue;
+
+		if ( e && e.preventDefault ) {
+			e.preventDefault();
+		}
+	},
+	stopPropagation: function() {
+		var e = this.originalEvent;
+
+		this.isPropagationStopped = returnTrue;
+
+		if ( e && e.stopPropagation ) {
+			e.stopPropagation();
+		}
+	},
+	stopImmediatePropagation: function() {
+		var e = this.originalEvent;
+
+		this.isImmediatePropagationStopped = returnTrue;
+
+		if ( e && e.stopImmediatePropagation ) {
+			e.stopImmediatePropagation();
+		}
+
+		this.stopPropagation();
+	}
+};
+
+// Create mouseenter/leave events using mouseover/out and event-time checks
+// Support: Chrome 15+
+jQuery.each({
+	mouseenter: "mouseover",
+	mouseleave: "mouseout",
+	pointerenter: "pointerover",
+	pointerleave: "pointerout"
+}, function( orig, fix ) {
+	jQuery.event.special[ orig ] = {
+		delegateType: fix,
+		bindType: fix,
+
+		handle: function( event ) {
+			var ret,
+				target = this,
+				related = event.relatedTarget,
+				handleObj = event.handleObj;
+
+			// For mousenter/leave call the handler if related is outside the target.
+			// NB: No relatedTarget if the mouse left/entered the browser window
+			if ( !related || (related !== target && !jQuery.contains( target, related )) ) {
+				event.type = handleObj.origType;
+				ret = handleObj.handler.apply( this, arguments );
+				event.type = fix;
+			}
+			return ret;
+		}
+	};
+});
+
+// Create "bubbling" focus and blur events
+// Support: Firefox, Chrome, Safari
+if ( !support.focusinBubbles ) {
+	jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) {
+
+		// Attach a single capturing handler on the document while someone wants focusin/focusout
+		var handler = function( event ) {
+				jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ), true );
+			};
+
+		jQuery.event.special[ fix ] = {
+			setup: function() {
+				var doc = this.ownerDocument || this,
+					attaches = data_priv.access( doc, fix );
+
+				if ( !attaches ) {
+					doc.addEventListener( orig, handler, true );
+				}
+				data_priv.access( doc, fix, ( attaches || 0 ) + 1 );
+			},
+			teardown: function() {
+				var doc = this.ownerDocument || this,
+					attaches = data_priv.access( doc, fix ) - 1;
+
+				if ( !attaches ) {
+					doc.removeEventListener( orig, handler, true );
+					data_priv.remove( doc, fix );
+
+				} else {
+					data_priv.access( doc, fix, attaches );
+				}
+			}
+		};
+	});
+}
+
+jQuery.fn.extend({
+
+	on: function( types, selector, data, fn, /*INTERNAL*/ one ) {
+		var origFn, type;
+
+		// Types can be a map of types/handlers
+		if ( typeof types === "object" ) {
+			// ( types-Object, selector, data )
+			if ( typeof selector !== "string" ) {
+				// ( types-Object, data )
+				data = data || selector;
+				selector = undefined;
+			}
+			for ( type in types ) {
+				this.on( type, selector, data, types[ type ], one );
+			}
+			return this;
+		}
+
+		if ( data == null && fn == null ) {
+			// ( types, fn )
+			fn = selector;
+			data = selector = undefined;
+		} else if ( fn == null ) {
+			if ( typeof selector === "string" ) {
+				// ( types, selector, fn )
+				fn = data;
+				data = undefined;
+			} else {
+				// ( types, data, fn )
+				fn = data;
+				data = selector;
+				selector = undefined;
+			}
+		}
+		if ( fn === false ) {
+			fn = returnFalse;
+		} else if ( !fn ) {
+			return this;
+		}
+
+		if ( one === 1 ) {
+			origFn = fn;
+			fn = function( event ) {
+				// Can use an empty set, since event contains the info
+				jQuery().off( event );
+				return origFn.apply( this, arguments );
+			};
+			// Use same guid so caller can remove using origFn
+			fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ );
+		}
+		return this.each( function() {
+			jQuery.event.add( this, types, fn, data, selector );
+		});
+	},
+	one: function( types, selector, data, fn ) {
+		return this.on( types, selector, data, fn, 1 );
+	},
+	off: function( types, selector, fn ) {
+		var handleObj, type;
+		if ( types && types.preventDefault && types.handleObj ) {
+			// ( event )  dispatched jQuery.Event
+			handleObj = types.handleObj;
+			jQuery( types.delegateTarget ).off(
+				handleObj.namespace ? handleObj.origType + "." + handleObj.namespace : handleObj.origType,
+				handleObj.selector,
+				handleObj.handler
+			);
+			return this;
+		}
+		if ( typeof types === "object" ) {
+			// ( types-object [, selector] )
+			for ( type in types ) {
+				this.off( type, selector, types[ type ] );
+			}
+			return this;
+		}
+		if ( selector === false || typeof selector === "function" ) {
+			// ( types [, fn] )
+			fn = selector;
+			selector = undefined;
+		}
+		if ( fn === false ) {
+			fn = returnFalse;
+		}
+		return this.each(function() {
+			jQuery.event.remove( this, types, fn, selector );
+		});
+	},
+
+	trigger: function( type, data ) {
+		return this.each(function() {
+			jQuery.event.trigger( type, data, this );
+		});
+	},
+	triggerHandler: function( type, data ) {
+		var elem = this[0];
+		if ( elem ) {
+			return jQuery.event.trigger( type, data, elem, true );
+		}
+	}
+});
+
+
+var
+	rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,
+	rtagName = /<([\w:]+)/,
+	rhtml = /<|&#?\w+;/,
+	rnoInnerhtml = /<(?:script|style|link)/i,
+	// checked="checked" or checked
+	rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i,
+	rscriptType = /^$|\/(?:java|ecma)script/i,
+	rscriptTypeMasked = /^true\/(.*)/,
+	rcleanScript = /^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g,
+
+	// We have to close these tags to support XHTML (#13200)
+	wrapMap = {
+
+		// Support: IE 9
+		option: [ 1, "<select multiple='multiple'>", "</select>" ],
+
+		thead: [ 1, "<table>", "</table>" ],
+		col: [ 2, "<table><colgroup>", "</colgroup></table>" ],
+		tr: [ 2, "<table><tbody>", "</tbody></table>" ],
+		td: [ 3, "<table><tbody><tr>", "</tr></tbody></table>" ],
+
+		_default: [ 0, "", "" ]
+	};
+
+// Support: IE 9
+wrapMap.optgroup = wrapMap.option;
+
+wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
+wrapMap.th = wrapMap.td;
+
+// Support: 1.x compatibility
+// Manipulating tables requires a tbody
+function manipulationTarget( elem, content ) {
+	return jQuery.nodeName( elem, "table" ) &&
+		jQuery.nodeName( content.nodeType !== 11 ? content : content.firstChild, "tr" ) ?
+
+		elem.getElementsByTagName("tbody")[0] ||
+			elem.appendChild( elem.ownerDocument.createElement("tbody") ) :
+		elem;
+}
+
+// Replace/restore the type attribute of script elements for safe DOM manipulation
+function disableScript( elem ) {
+	elem.type = (elem.getAttribute("type") !== null) + "/" + elem.type;
+	return elem;
+}
+function restoreScript( elem ) {
+	var match = rscriptTypeMasked.exec( elem.type );
+
+	if ( match ) {
+		elem.type = match[ 1 ];
+	} else {
+		elem.removeAttribute("type");
+	}
+
+	return elem;
+}
+
+// Mark scripts as having already been evaluated
+function setGlobalEval( elems, refElements ) {
+	var i = 0,
+		l = elems.length;
+
+	for ( ; i < l; i++ ) {
+		data_priv.set(
+			elems[ i ], "globalEval", !refElements || data_priv.get( refElements[ i ], "globalEval" )
+		);
+	}
+}
+
+function cloneCopyEvent( src, dest ) {
+	var i, l, type, pdataOld, pdataCur, udataOld, udataCur, events;
+
+	if ( dest.nodeType !== 1 ) {
+		return;
+	}
+
+	// 1. Copy private data: events, handlers, etc.
+	if ( data_priv.hasData( src ) ) {
+		pdataOld = data_priv.access( src );
+		pdataCur = data_priv.set( dest, pdataOld );
+		events = pdataOld.events;
+
+		if ( events ) {
+			delete pdataCur.handle;
+			pdataCur.events = {};
+
+			for ( type in events ) {
+				for ( i = 0, l = events[ type ].length; i < l; i++ ) {
+					jQuery.event.add( dest, type, events[ type ][ i ] );
+				}
+			}
+		}
+	}
+
+	// 2. Copy user data
+	if ( data_user.hasData( src ) ) {
+		udataOld = data_user.access( src );
+		udataCur = jQuery.extend( {}, udataOld );
+
+		data_user.set( dest, udataCur );
+	}
+}
+
+function getAll( context, tag ) {
+	var ret = context.getElementsByTagName ? context.getElementsByTagName( tag || "*" ) :
+			context.querySelectorAll ? context.querySelectorAll( tag || "*" ) :
+			[];
+
+	return tag === undefined || tag && jQuery.nodeName( context, tag ) ?
+		jQuery.merge( [ context ], ret ) :
+		ret;
+}
+
+// Support: IE >= 9
+function fixInput( src, dest ) {
+	var nodeName = dest.nodeName.toLowerCase();
+
+	// Fails to persist the checked state of a cloned checkbox or radio button.
+	if ( nodeName === "input" && rcheckableType.test( src.type ) ) {
+		dest.checked = src.checked;
+
+	// Fails to return the selected option to the default selected state when cloning options
+	} else if ( nodeName === "input" || nodeName === "textarea" ) {
+		dest.defaultValue = src.defaultValue;
+	}
+}
+
+jQuery.extend({
+	clone: function( elem, dataAndEvents, deepDataAndEvents ) {
+		var i, l, srcElements, destElements,
+			clone = elem.cloneNode( true ),
+			inPage = jQuery.contains( elem.ownerDocument, elem );
+
+		// Support: IE >= 9
+		// Fix Cloning issues
+		if ( !support.noCloneChecked && ( elem.nodeType === 1 || elem.nodeType === 11 ) &&
+				!jQuery.isXMLDoc( elem ) ) {
+
+			// We eschew Sizzle here for performance reasons: http://jsperf.com/getall-vs-sizzle/2
+			destElements = getAll( clone );
+			srcElements = getAll( elem );
+
+			for ( i = 0, l = srcElements.length; i < l; i++ ) {
+				fixInput( srcElements[ i ], destElements[ i ] );
+			}
+		}
+
+		// Copy the events from the original to the clone
+		if ( dataAndEvents ) {
+			if ( deepDataAndEvents ) {
+				srcElements = srcElements || getAll( elem );
+				destElements = destElements || getAll( clone );
+
+				for ( i = 0, l = srcElements.length; i < l; i++ ) {
+					cloneCopyEvent( srcElements[ i ], destElements[ i ] );
+				}
+			} else {
+				cloneCopyEvent( elem, clone );
+			}
+		}
+
+		// Preserve script evaluation history
+		destElements = getAll( clone, "script" );
+		if ( destElements.length > 0 ) {
+			setGlobalEval( destElements, !inPage && getAll( elem, "script" ) );
+		}
+
+		// Return the cloned set
+		return clone;
+	},
+
+	buildFragment: function( elems, context, scripts, selection ) {
+		var elem, tmp, tag, wrap, contains, j,
+			fragment = context.createDocumentFragment(),
+			nodes = [],
+			i = 0,
+			l = elems.length;
+
+		for ( ; i < l; i++ ) {
+			elem = elems[ i ];
+
+			if ( elem || elem === 0 ) {
+
+				// Add nodes directly
+				if ( jQuery.type( elem ) === "object" ) {
+					// Support: QtWebKit
+					// jQuery.merge because push.apply(_, arraylike) throws
+					jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem );
+
+				// Convert non-html into a text node
+				} else if ( !rhtml.test( elem ) ) {
+					nodes.push( context.createTextNode( elem ) );
+
+				// Convert html into DOM nodes
+				} else {
+					tmp = tmp || fragment.appendChild( context.createElement("div") );
+
+					// Deserialize a standard representation
+					tag = ( rtagName.exec( elem ) || [ "", "" ] )[ 1 ].toLowerCase();
+					wrap = wrapMap[ tag ] || wrapMap._default;
+					tmp.innerHTML = wrap[ 1 ] + elem.replace( rxhtmlTag, "<$1></$2>" ) + wrap[ 2 ];
+
+					// Descend through wrappers to the right content
+					j = wrap[ 0 ];
+					while ( j-- ) {
+						tmp = tmp.lastChild;
+					}
+
+					// Support: QtWebKit
+					// jQuery.merge because push.apply(_, arraylike) throws
+					jQuery.merge( nodes, tmp.childNodes );
+
+					// Remember the top-level container
+					tmp = fragment.firstChild;
+
+					// Fixes #12346
+					// Support: Webkit, IE
+					tmp.textContent = "";
+				}
+			}
+		}
+
+		// Remove wrapper from fragment
+		fragment.textContent = "";
+
+		i = 0;
+		while ( (elem = nodes[ i++ ]) ) {
+
+			// #4087 - If origin and destination elements are the same, and this is
+			// that element, do not do anything
+			if ( selection && jQuery.inArray( elem, selection ) !== -1 ) {
+				continue;
+			}
+
+			contains = jQuery.contains( elem.ownerDocument, elem );
+
+			// Append to fragment
+			tmp = getAll( fragment.appendChild( elem ), "script" );
+
+			// Preserve script evaluation history
+			if ( contains ) {
+				setGlobalEval( tmp );
+			}
+
+			// Capture executables
+			if ( scripts ) {
+				j = 0;
+				while ( (elem = tmp[ j++ ]) ) {
+					if ( rscriptType.test( elem.type || "" ) ) {
+						scripts.push( elem );
+					}
+				}
+			}
+		}
+
+		return fragment;
+	},
+
+	cleanData: function( elems ) {
+		var data, elem, type, key,
+			special = jQuery.event.special,
+			i = 0;
+
+		for ( ; (elem = elems[ i ]) !== undefined; i++ ) {
+			if ( jQuery.acceptData( elem ) ) {
+				key = elem[ data_priv.expando ];
+
+				if ( key && (data = data_priv.cache[ key ]) ) {
+					if ( data.events ) {
+						for ( type in data.events ) {
+							if ( special[ type ] ) {
+								jQuery.event.remove( elem, type );
+
+							// This is a shortcut to avoid jQuery.event.remove's overhead
+							} else {
+								jQuery.removeEvent( elem, type, data.handle );
+							}
+						}
+					}
+					if ( data_priv.cache[ key ] ) {
+						// Discard any remaining `private` data
+						delete data_priv.cache[ key ];
+					}
+				}
+			}
+			// Discard any remaining `user` data
+			delete data_user.cache[ elem[ data_user.expando ] ];
+		}
+	}
+});
+
+jQuery.fn.extend({
+	text: function( value ) {
+		return access( this, function( value ) {
+			return value === undefined ?
+				jQuery.text( this ) :
+				this.empty().each(function() {
+					if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {
+						this.textContent = value;
+					}
+				});
+		}, null, value, arguments.length );
+	},
+
+	append: function() {
+		return this.domManip( arguments, function( elem ) {
+			if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {
+				var target = manipulationTarget( this, elem );
+				target.appendChild( elem );
+			}
+		});
+	},
+
+	prepend: function() {
+		return this.domManip( arguments, function( elem ) {
+			if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {
+				var target = manipulationTarget( this, elem );
+				target.insertBefore( elem, target.firstChild );
+			}
+		});
+	},
+
+	before: function() {
+		return this.domManip( arguments, function( elem ) {
+			if ( this.parentNode ) {
+				this.parentNode.insertBefore( elem, this );
+			}
+		});
+	},
+
+	after: function() {
+		return this.domManip( arguments, function( elem ) {
+			if ( this.parentNode ) {
+				this.parentNode.insertBefore( elem, this.nextSibling );
+			}
+		});
+	},
+
+	remove: function( selector, keepData /* Internal Use Only */ ) {
+		var elem,
+			elems = selector ? jQuery.filter( selector, this ) : this,
+			i = 0;
+
+		for ( ; (elem = elems[i]) != null; i++ ) {
+			if ( !keepData && elem.nodeType === 1 ) {
+				jQuery.cleanData( getAll( elem ) );
+			}
+
+			if ( elem.parentNode ) {
+				if ( keepData && jQuery.contains( elem.ownerDocument, elem ) ) {
+					setGlobalEval( getAll( elem, "script" ) );
+				}
+				elem.parentNode.removeChild( elem );
+			}
+		}
+
+		return this;
+	},
+
+	empty: function() {
+		var elem,
+			i = 0;
+
+		for ( ; (elem = this[i]) != null; i++ ) {
+			if ( elem.nodeType === 1 ) {
+
+				// Prevent memory leaks
+				jQuery.cleanData( getAll( elem, false ) );
+
+				// Remove any remaining nodes
+				elem.textContent = "";
+			}
+		}
+
+		return this;
+	},
+
+	clone: function( dataAndEvents, deepDataAndEvents ) {
+		dataAndEvents = dataAndEvents == null ? false : dataAndEvents;
+		deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents;
+
+		return this.map(function() {
+			return jQuery.clone( this, dataAndEvents, deepDataAndEvents );
+		});
+	},
+
+	html: function( value ) {
+		return access( this, function( value ) {
+			var elem = this[ 0 ] || {},
+				i = 0,
+				l = this.length;
+
+			if ( value === undefined && elem.nodeType === 1 ) {
+				return elem.innerHTML;
+			}
+
+			// See if we can take a shortcut and just use innerHTML
+			if ( typeof value === "string" && !rnoInnerhtml.test( value ) &&
+				!wrapMap[ ( rtagName.exec( value ) || [ "", "" ] )[ 1 ].toLowerCase() ] ) {
+
+				value = value.replace( rxhtmlTag, "<$1></$2>" );
+
+				try {
+					for ( ; i < l; i++ ) {
+						elem = this[ i ] || {};
+
+						// Remove element nodes and prevent memory leaks
+						if ( elem.nodeType === 1 ) {
+							jQuery.cleanData( getAll( elem, false ) );
+							elem.innerHTML = value;
+						}
+					}
+
+					elem = 0;
+
+				// If using innerHTML throws an exception, use the fallback method
+				} catch( e ) {}
+			}
+
+			if ( elem ) {
+				this.empty().append( value );
+			}
+		}, null, value, arguments.length );
+	},
+
+	replaceWith: function() {
+		var arg = arguments[ 0 ];
+
+		// Make the changes, replacing each context element with the new content
+		this.domManip( arguments, function( elem ) {
+			arg = this.parentNode;
+
+			jQuery.cleanData( getAll( this ) );
+
+			if ( arg ) {
+				arg.replaceChild( elem, this );
+			}
+		});
+
+		// Force removal if there was no new content (e.g., from empty arguments)
+		return arg && (arg.length || arg.nodeType) ? this : this.remove();
+	},
+
+	detach: function( selector ) {
+		return this.remove( selector, true );
+	},
+
+	domManip: function( args, callback ) {
+
+		// Flatten any nested arrays
+		args = concat.apply( [], args );
+
+		var fragment, first, scripts, hasScripts, node, doc,
+			i = 0,
+			l = this.length,
+			set = this,
+			iNoClone = l - 1,
+			value = args[ 0 ],
+			isFunction = jQuery.isFunction( value );
+
+		// We can't cloneNode fragments that contain checked, in WebKit
+		if ( isFunction ||
+				( l > 1 && typeof value === "string" &&
+					!support.checkClone && rchecked.test( value ) ) ) {
+			return this.each(function( index ) {
+				var self = set.eq( index );
+				if ( isFunction ) {
+					args[ 0 ] = value.call( this, index, self.html() );
+				}
+				self.domManip( args, callback );
+			});
+		}
+
+		if ( l ) {
+			fragment = jQuery.buildFragment( args, this[ 0 ].ownerDocument, false, this );
+			first = fragment.firstChild;
+
+			if ( fragment.childNodes.length === 1 ) {
+				fragment = first;
+			}
+
+			if ( first ) {
+				scripts = jQuery.map( getAll( fragment, "script" ), disableScript );
+				hasScripts = scripts.length;
+
+				// Use the original fragment for the last item instead of the first because it can end up
+				// being emptied incorrectly in certain situations (#8070).
+				for ( ; i < l; i++ ) {
+					node = fragment;
+
+					if ( i !== iNoClone ) {
+						node = jQuery.clone( node, true, true );
+
+						// Keep references to cloned scripts for later restoration
+						if ( hasScripts ) {
+							// Support: QtWebKit
+							// jQuery.merge because push.apply(_, arraylike) throws
+							jQuery.merge( scripts, getAll( node, "script" ) );
+						}
+					}
+
+					callback.call( this[ i ], node, i );
+				}
+
+				if ( hasScripts ) {
+					doc = scripts[ scripts.length - 1 ].ownerDocument;
+
+					// Reenable scripts
+					jQuery.map( scripts, restoreScript );
+
+					// Evaluate executable scripts on first document insertion
+					for ( i = 0; i < hasScripts; i++ ) {
+						node = scripts[ i ];
+						if ( rscriptType.test( node.type || "" ) &&
+							!data_priv.access( node, "globalEval" ) && jQuery.contains( doc, node ) ) {
+
+							if ( node.src ) {
+								// Optional AJAX dependency, but won't run scripts if not present
+								if ( jQuery._evalUrl ) {
+									jQuery._evalUrl( node.src );
+								}
+							} else {
+								jQuery.globalEval( node.textContent.replace( rcleanScript, "" ) );
+							}
+						}
+					}
+				}
+			}
+		}
+
+		return this;
+	}
+});
+
+jQuery.each({
+	appendTo: "append",
+	prependTo: "prepend",
+	insertBefore: "before",
+	insertAfter: "after",
+	replaceAll: "replaceWith"
+}, function( name, original ) {
+	jQuery.fn[ name ] = function( selector ) {
+		var elems,
+			ret = [],
+			insert = jQuery( selector ),
+			last = insert.length - 1,
+			i = 0;
+
+		for ( ; i <= last; i++ ) {
+			elems = i === last ? this : this.clone( true );
+			jQuery( insert[ i ] )[ original ]( elems );
+
+			// Support: QtWebKit
+			// .get() because push.apply(_, arraylike) throws
+			push.apply( ret, elems.get() );
+		}
+
+		return this.pushStack( ret );
+	};
+});
+
+
+var iframe,
+	elemdisplay = {};
+
+/**
+ * Retrieve the actual display of a element
+ * @param {String} name nodeName of the element
+ * @param {Object} doc Document object
+ */
+// Called only from within defaultDisplay
+function actualDisplay( name, doc ) {
+	var style,
+		elem = jQuery( doc.createElement( name ) ).appendTo( doc.body ),
+
+		// getDefaultComputedStyle might be reliably used only on attached element
+		display = window.getDefaultComputedStyle && ( style = window.getDefaultComputedStyle( elem[ 0 ] ) ) ?
+
+			// Use of this method is a temporary fix (more like optmization) until something better comes along,
+			// since it was removed from specification and supported only in FF
+			style.display : jQuery.css( elem[ 0 ], "display" );
+
+	// We don't have any data stored on the element,
+	// so use "detach" method as fast way to get rid of the element
+	elem.detach();
+
+	return display;
+}
+
+/**
+ * Try to determine the default display value of an element
+ * @param {String} nodeName
+ */
+function defaultDisplay( nodeName ) {
+	var doc = document,
+		display = elemdisplay[ nodeName ];
+
+	if ( !display ) {
+		display = actualDisplay( nodeName, doc );
+
+		// If the simple way fails, read from inside an iframe
+		if ( display === "none" || !display ) {
+
+			// Use the already-created iframe if possible
+			iframe = (iframe || jQuery( "<iframe frameborder='0' width='0' height='0'/>" )).appendTo( doc.documentElement );
+
+			// Always write a new HTML skeleton so Webkit and Firefox don't choke on reuse
+			doc = iframe[ 0 ].contentDocument;
+
+			// Support: IE
+			doc.write();
+			doc.close();
+
+			display = actualDisplay( nodeName, doc );
+			iframe.detach();
+		}
+
+		// Store the correct default display
+		elemdisplay[ nodeName ] = display;
+	}
+
+	return display;
+}
+var rmargin = (/^margin/);
+
+var rnumnonpx = new RegExp( "^(" + pnum + ")(?!px)[a-z%]+$", "i" );
+
+var getStyles = function( elem ) {
+		return elem.ownerDocument.defaultView.getComputedStyle( elem, null );
+	};
+
+
+
+function curCSS( elem, name, computed ) {
+	var width, minWidth, maxWidth, ret,
+		style = elem.style;
+
+	computed = computed || getStyles( elem );
+
+	// Support: IE9
+	// getPropertyValue is only needed for .css('filter') in IE9, see #12537
+	if ( computed ) {
+		ret = computed.getPropertyValue( name ) || computed[ name ];
+	}
+
+	if ( computed ) {
+
+		if ( ret === "" && !jQuery.contains( elem.ownerDocument, elem ) ) {
+			ret = jQuery.style( elem, name );
+		}
+
+		// Support: iOS < 6
+		// A tribute to the "awesome hack by Dean Edwards"
+		// iOS < 6 (at least) returns percentage for a larger set of values, but width seems to be reliably pixels
+		// this is against the CSSOM draft spec: http://dev.w3.org/csswg/cssom/#resolved-values
+		if ( rnumnonpx.test( ret ) && rmargin.test( name ) ) {
+
+			// Remember the original values
+			width = style.width;
+			minWidth = style.minWidth;
+			maxWidth = style.maxWidth;
+
+			// Put in the new values to get a computed value out
+			style.minWidth = style.maxWidth = style.width = ret;
+			ret = computed.width;
+
+			// Revert the changed values
+			style.width = width;
+			style.minWidth = minWidth;
+			style.maxWidth = maxWidth;
+		}
+	}
+
+	return ret !== undefined ?
+		// Support: IE
+		// IE returns zIndex value as an integer.
+		ret + "" :
+		ret;
+}
+
+
+function addGetHookIf( conditionFn, hookFn ) {
+	// Define the hook, we'll check on the first run if it's really needed.
+	return {
+		get: function() {
+			if ( conditionFn() ) {
+				// Hook not needed (or it's not possible to use it due to missing dependency),
+				// remove it.
+				// Since there are no other hooks for marginRight, remove the whole object.
+				delete this.get;
+				return;
+			}
+
+			// Hook needed; redefine it so that the support test is not executed again.
+
+			return (this.get = hookFn).apply( this, arguments );
+		}
+	};
+}
+
+
+(function() {
+	var pixelPositionVal, boxSizingReliableVal,
+		docElem = document.documentElement,
+		container = document.createElement( "div" ),
+		div = document.createElement( "div" );
+
+	if ( !div.style ) {
+		return;
+	}
+
+	div.style.backgroundClip = "content-box";
+	div.cloneNode( true ).style.backgroundClip = "";
+	support.clearCloneStyle = div.style.backgroundClip === "content-box";
+
+	container.style.cssText = "border:0;width:0;height:0;top:0;left:-9999px;margin-top:1px;" +
+		"position:absolute";
+	container.appendChild( div );
+
+	// Executing both pixelPosition & boxSizingReliable tests require only one layout
+	// so they're executed at the same time to save the second computation.
+	function computePixelPositionAndBoxSizingReliable() {
+		div.style.cssText =
+			// Support: Firefox<29, Android 2.3
+			// Vendor-prefix box-sizing
+			"-webkit-box-sizing:border-box;-moz-box-sizing:border-box;" +
+			"box-sizing:border-box;display:block;margin-top:1%;top:1%;" +
+			"border:1px;padding:1px;width:4px;position:absolute";
+		div.innerHTML = "";
+		docElem.appendChild( container );
+
+		var divStyle = window.getComputedStyle( div, null );
+		pixelPositionVal = divStyle.top !== "1%";
+		boxSizingReliableVal = divStyle.width === "4px";
+
+		docElem.removeChild( container );
+	}
+
+	// Support: node.js jsdom
+	// Don't assume that getComputedStyle is a property of the global object
+	if ( window.getComputedStyle ) {
+		jQuery.extend( support, {
+			pixelPosition: function() {
+				// This test is executed only once but we still do memoizing
+				// since we can use the boxSizingReliable pre-computing.
+				// No need to check if the test was already performed, though.
+				computePixelPositionAndBoxSizingReliable();
+				return pixelPositionVal;
+			},
+			boxSizingReliable: function() {
+				if ( boxSizingReliableVal == null ) {
+					computePixelPositionAndBoxSizingReliable();
+				}
+				return boxSizingReliableVal;
+			},
+			reliableMarginRight: function() {
+				// Support: Android 2.3
+				// Check if div with explicit width and no margin-right incorrectly
+				// gets computed margin-right based on width of container. (#3333)
+				// WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right
+				// This support function is only executed once so no memoizing is needed.
+				var ret,
+					marginDiv = div.appendChild( document.createElement( "div" ) );
+
+				// Reset CSS: box-sizing; display; margin; border; padding
+				marginDiv.style.cssText = div.style.cssText =
+					// Support: Firefox<29, Android 2.3
+					// Vendor-prefix box-sizing
+					"-webkit-box-sizing:content-box;-moz-box-sizing:content-box;" +
+					"box-sizing:content-box;display:block;margin:0;border:0;padding:0";
+				marginDiv.style.marginRight = marginDiv.style.width = "0";
+				div.style.width = "1px";
+				docElem.appendChild( container );
+
+				ret = !parseFloat( window.getComputedStyle( marginDiv, null ).marginRight );
+
+				docElem.removeChild( container );
+
+				return ret;
+			}
+		});
+	}
+})();
+
+
+// A method for quickly swapping in/out CSS properties to get correct calculations.
+jQuery.swap = function( elem, options, callback, args ) {
+	var ret, name,
+		old = {};
+
+	// Remember the old values, and insert the new ones
+	for ( name in options ) {
+		old[ name ] = elem.style[ name ];
+		elem.style[ name ] = options[ name ];
+	}
+
+	ret = callback.apply( elem, args || [] );
+
+	// Revert the old values
+	for ( name in options ) {
+		elem.style[ name ] = old[ name ];
+	}
+
+	return ret;
+};
+
+
+var
+	// swappable if display is none or starts with table except "table", "table-cell", or "table-caption"
+	// see here for display values: https://developer.mozilla.org/en-US/docs/CSS/display
+	rdisplayswap = /^(none|table(?!-c[ea]).+)/,
+	rnumsplit = new RegExp( "^(" + pnum + ")(.*)$", "i" ),
+	rrelNum = new RegExp( "^([+-])=(" + pnum + ")", "i" ),
+
+	cssShow = { position: "absolute", visibility: "hidden", display: "block" },
+	cssNormalTransform = {
+		letterSpacing: "0",
+		fontWeight: "400"
+	},
+
+	cssPrefixes = [ "Webkit", "O", "Moz", "ms" ];
+
+// return a css property mapped to a potentially vendor prefixed property
+function vendorPropName( style, name ) {
+
+	// shortcut for names that are not vendor prefixed
+	if ( name in style ) {
+		return name;
+	}
+
+	// check for vendor prefixed names
+	var capName = name[0].toUpperCase() + name.slice(1),
+		origName = name,
+		i = cssPrefixes.length;
+
+	while ( i-- ) {
+		name = cssPrefixes[ i ] + capName;
+		if ( name in style ) {
+			return name;
+		}
+	}
+
+	return origName;
+}
+
+function setPositiveNumber( elem, value, subtract ) {
+	var matches = rnumsplit.exec( value );
+	return matches ?
+		// Guard against undefined "subtract", e.g., when used as in cssHooks
+		Math.max( 0, matches[ 1 ] - ( subtract || 0 ) ) + ( matches[ 2 ] || "px" ) :
+		value;
+}
+
+function augmentWidthOrHeight( elem, name, extra, isBorderBox, styles ) {
+	var i = extra === ( isBorderBox ? "border" : "content" ) ?
+		// If we already have the right measurement, avoid augmentation
+		4 :
+		// Otherwise initialize for horizontal or vertical properties
+		name === "width" ? 1 : 0,
+
+		val = 0;
+
+	for ( ; i < 4; i += 2 ) {
+		// both box models exclude margin, so add it if we want it
+		if ( extra === "margin" ) {
+			val += jQuery.css( elem, extra + cssExpand[ i ], true, styles );
+		}
+
+		if ( isBorderBox ) {
+			// border-box includes padding, so remove it if we want content
+			if ( extra === "content" ) {
+				val -= jQuery.css( elem, "padding" + cssExpand[ i ], true, styles );
+			}
+
+			// at this point, extra isn't border nor margin, so remove border
+			if ( extra !== "margin" ) {
+				val -= jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles );
+			}
+		} else {
+			// at this point, extra isn't content, so add padding
+			val += jQuery.css( elem, "padding" + cssExpand[ i ], true, styles );
+
+			// at this point, extra isn't content nor padding, so add border
+			if ( extra !== "padding" ) {
+				val += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles );
+			}
+		}
+	}
+
+	return val;
+}
+
+function getWidthOrHeight( elem, name, extra ) {
+
+	// Start with offset property, which is equivalent to the border-box value
+	var valueIsBorderBox = true,
+		val = name === "width" ? elem.offsetWidth : elem.offsetHeight,
+		styles = getStyles( elem ),
+		isBorderBox = jQuery.css( elem, "boxSizing", false, styles ) === "border-box";
+
+	// some non-html elements return undefined for offsetWidth, so check for null/undefined
+	// svg - https://bugzilla.mozilla.org/show_bug.cgi?id=649285
+	// MathML - https://bugzilla.mozilla.org/show_bug.cgi?id=491668
+	if ( val <= 0 || val == null ) {
+		// Fall back to computed then uncomputed css if necessary
+		val = curCSS( elem, name, styles );
+		if ( val < 0 || val == null ) {
+			val = elem.style[ name ];
+		}
+
+		// Computed unit is not pixels. Stop here and return.
+		if ( rnumnonpx.test(val) ) {
+			return val;
+		}
+
+		// we need the check for style in case a browser which returns unreliable values
+		// for getComputedStyle silently falls back to the reliable elem.style
+		valueIsBorderBox = isBorderBox &&
+			( support.boxSizingReliable() || val === elem.style[ name ] );
+
+		// Normalize "", auto, and prepare for extra
+		val = parseFloat( val ) || 0;
+	}
+
+	// use the active box-sizing model to add/subtract irrelevant styles
+	return ( val +
+		augmentWidthOrHeight(
+			elem,
+			name,
+			extra || ( isBorderBox ? "border" : "content" ),
+			valueIsBorderBox,
+			styles
+		)
+	) + "px";
+}
+
+function showHide( elements, show ) {
+	var display, elem, hidden,
+		values = [],
+		index = 0,
+		length = elements.length;
+
+	for ( ; index < length; index++ ) {
+		elem = elements[ index ];
+		if ( !elem.style ) {
+			continue;
+		}
+
+		values[ index ] = data_priv.get( elem, "olddisplay" );
+		display = elem.style.display;
+		if ( show ) {
+			// Reset the inline display of this element to learn if it is
+			// being hidden by cascaded rules or not
+			if ( !values[ index ] && display === "none" ) {
+				elem.style.display = "";
+			}
+
+			// Set elements which have been overridden with display: none
+			// in a stylesheet to whatever the default browser style is
+			// for such an element
+			if ( elem.style.display === "" && isHidden( elem ) ) {
+				values[ index ] = data_priv.access( elem, "olddisplay", defaultDisplay(elem.nodeName) );
+			}
+		} else {
+			hidden = isHidden( elem );
+
+			if ( display !== "none" || !hidden ) {
+				data_priv.set( elem, "olddisplay", hidden ? display : jQuery.css( elem, "display" ) );
+			}
+		}
+	}
+
+	// Set the display of most of the elements in a second loop
+	// to avoid the constant reflow
+	for ( index = 0; index < length; index++ ) {
+		elem = elements[ index ];
+		if ( !elem.style ) {
+			continue;
+		}
+		if ( !show || elem.style.display === "none" || elem.style.display === "" ) {
+			elem.style.display = show ? values[ index ] || "" : "none";
+		}
+	}
+
+	return elements;
+}
+
+jQuery.extend({
+	// Add in style property hooks for overriding the default
+	// behavior of getting and setting a style property
+	cssHooks: {
+		opacity: {
+			get: function( elem, computed ) {
+				if ( computed ) {
+					// We should always get a number back from opacity
+					var ret = curCSS( elem, "opacity" );
+					return ret === "" ? "1" : ret;
+				}
+			}
+		}
+	},
+
+	// Don't automatically add "px" to these possibly-unitless properties
+	cssNumber: {
+		"columnCount": true,
+		"fillOpacity": true,
+		"flexGrow": true,
+		"flexShrink": true,
+		"fontWeight": true,
+		"lineHeight": true,
+		"opacity": true,
+		"order": true,
+		"orphans": true,
+		"widows": true,
+		"zIndex": true,
+		"zoom": true
+	},
+
+	// Add in properties whose names you wish to fix before
+	// setting or getting the value
+	cssProps: {
+		// normalize float css property
+		"float": "cssFloat"
+	},
+
+	// Get and set the style property on a DOM Node
+	style: function( elem, name, value, extra ) {
+		// Don't set styles on text and comment nodes
+		if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) {
+			return;
+		}
+
+		// Make sure that we're working with the right name
+		var ret, type, hooks,
+			origName = jQuery.camelCase( name ),
+			style = elem.style;
+
+		name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( style, origName ) );
+
+		// gets hook for the prefixed version
+		// followed by the unprefixed version
+		hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ];
+
+		// Check if we're setting a value
+		if ( value !== undefined ) {
+			type = typeof value;
+
+			// convert relative number strings (+= or -=) to relative numbers. #7345
+			if ( type === "string" && (ret = rrelNum.exec( value )) ) {
+				value = ( ret[1] + 1 ) * ret[2] + parseFloat( jQuery.css( elem, name ) );
+				// Fixes bug #9237
+				type = "number";
+			}
+
+			// Make sure that null and NaN values aren't set. See: #7116
+			if ( value == null || value !== value ) {
+				return;
+			}
+
+			// If a number was passed in, add 'px' to the (except for certain CSS properties)
+			if ( type === "number" && !jQuery.cssNumber[ origName ] ) {
+				value += "px";
+			}
+
+			// Fixes #8908, it can be done more correctly by specifying setters in cssHooks,
+			// but it would mean to define eight (for every problematic property) identical functions
+			if ( !support.clearCloneStyle && value === "" && name.indexOf( "background" ) === 0 ) {
+				style[ name ] = "inherit";
+			}
+
+			// If a hook was provided, use that value, otherwise just set the specified value
+			if ( !hooks || !("set" in hooks) || (value = hooks.set( elem, value, extra )) !== undefined ) {
+				style[ name ] = value;
+			}
+
+		} else {
+			// If a hook was provided get the non-computed value from there
+			if ( hooks && "get" in hooks && (ret = hooks.get( elem, false, extra )) !== undefined ) {
+				return ret;
+			}
+
+			// Otherwise just get the value from the style object
+			return style[ name ];
+		}
+	},
+
+	css: function( elem, name, extra, styles ) {
+		var val, num, hooks,
+			origName = jQuery.camelCase( name );
+
+		// Make sure that we're working with the right name
+		name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( elem.style, origName ) );
+
+		// gets hook for the prefixed version
+		// followed by the unprefixed version
+		hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ];
+
+		// If a hook was provided get the computed value from there
+		if ( hooks && "get" in hooks ) {
+			val = hooks.get( elem, true, extra );
+		}
+
+		// Otherwise, if a way to get the computed value exists, use that
+		if ( val === undefined ) {
+			val = curCSS( elem, name, styles );
+		}
+
+		//convert "normal" to computed value
+		if ( val === "normal" && name in cssNormalTransform ) {
+			val = cssNormalTransform[ name ];
+		}
+
+		// Return, converting to number if forced or a qualifier was provided and val looks numeric
+		if ( extra === "" || extra ) {
+			num = parseFloat( val );
+			return extra === true || jQuery.isNumeric( num ) ? num || 0 : val;
+		}
+		return val;
+	}
+});
+
+jQuery.each([ "height", "width" ], function( i, name ) {
+	jQuery.cssHooks[ name ] = {
+		get: function( elem, computed, extra ) {
+			if ( computed ) {
+				// certain elements can have dimension info if we invisibly show them
+				// however, it must have a current display style that would benefit from this
+				return rdisplayswap.test( jQuery.css( elem, "display" ) ) && elem.offsetWidth === 0 ?
+					jQuery.swap( elem, cssShow, function() {
+						return getWidthOrHeight( elem, name, extra );
+					}) :
+					getWidthOrHeight( elem, name, extra );
+			}
+		},
+
+		set: function( elem, value, extra ) {
+			var styles = extra && getStyles( elem );
+			return setPositiveNumber( elem, value, extra ?
+				augmentWidthOrHeight(
+					elem,
+					name,
+					extra,
+					jQuery.css( elem, "boxSizing", false, styles ) === "border-box",
+					styles
+				) : 0
+			);
+		}
+	};
+});
+
+// Support: Android 2.3
+jQuery.cssHooks.marginRight = addGetHookIf( support.reliableMarginRight,
+	function( elem, computed ) {
+		if ( computed ) {
+			// WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right
+			// Work around by temporarily setting element display to inline-block
+			return jQuery.swap( elem, { "display": "inline-block" },
+				curCSS, [ elem, "marginRight" ] );
+		}
+	}
+);
+
+// These hooks are used by animate to expand properties
+jQuery.each({
+	margin: "",
+	padding: "",
+	border: "Width"
+}, function( prefix, suffix ) {
+	jQuery.cssHooks[ prefix + suffix ] = {
+		expand: function( value ) {
+			var i = 0,
+				expanded = {},
+
+				// assumes a single number if not a string
+				parts = typeof value === "string" ? value.split(" ") : [ value ];
+
+			for ( ; i < 4; i++ ) {
+				expanded[ prefix + cssExpand[ i ] + suffix ] =
+					parts[ i ] || parts[ i - 2 ] || parts[ 0 ];
+			}
+
+			return expanded;
+		}
+	};
+
+	if ( !rmargin.test( prefix ) ) {
+		jQuery.cssHooks[ prefix + suffix ].set = setPositiveNumber;
+	}
+});
+
+jQuery.fn.extend({
+	css: function( name, value ) {
+		return access( this, function( elem, name, value ) {
+			var styles, len,
+				map = {},
+				i = 0;
+
+			if ( jQuery.isArray( name ) ) {
+				styles = getStyles( elem );
+				len = name.length;
+
+				for ( ; i < len; i++ ) {
+					map[ name[ i ] ] = jQuery.css( elem, name[ i ], false, styles );
+				}
+
+				return map;
+			}
+
+			return value !== undefined ?
+				jQuery.style( elem, name, value ) :
+				jQuery.css( elem, name );
+		}, name, value, arguments.length > 1 );
+	},
+	show: function() {
+		return showHide( this, true );
+	},
+	hide: function() {
+		return showHide( this );
+	},
+	toggle: function( state ) {
+		if ( typeof state === "boolean" ) {
+			return state ? this.show() : this.hide();
+		}
+
+		return this.each(function() {
+			if ( isHidden( this ) ) {
+				jQuery( this ).show();
+			} else {
+				jQuery( this ).hide();
+			}
+		});
+	}
+});
+
+
+function Tween( elem, options, prop, end, easing ) {
+	return new Tween.prototype.init( elem, options, prop, end, easing );
+}
+jQuery.Tween = Tween;
+
+Tween.prototype = {
+	constructor: Tween,
+	init: function( elem, options, prop, end, easing, unit ) {
+		this.elem = elem;
+		this.prop = prop;
+		this.easing = easing || "swing";
+		this.options = options;
+		this.start = this.now = this.cur();
+		this.end = end;
+		this.unit = unit || ( jQuery.cssNumber[ prop ] ? "" : "px" );
+	},
+	cur: function() {
+		var hooks = Tween.propHooks[ this.prop ];
+
+		return hooks && hooks.get ?
+			hooks.get( this ) :
+			Tween.propHooks._default.get( this );
+	},
+	run: function( percent ) {
+		var eased,
+			hooks = Tween.propHooks[ this.prop ];
+
+		if ( this.options.duration ) {
+			this.pos = eased = jQuery.easing[ this.easing ](
+				percent, this.options.duration * percent, 0, 1, this.options.duration
+			);
+		} else {
+			this.pos = eased = percent;
+		}
+		this.now = ( this.end - this.start ) * eased + this.start;
+
+		if ( this.options.step ) {
+			this.options.step.call( this.elem, this.now, this );
+		}
+
+		if ( hooks && hooks.set ) {
+			hooks.set( this );
+		} else {
+			Tween.propHooks._default.set( this );
+		}
+		return this;
+	}
+};
+
+Tween.prototype.init.prototype = Tween.prototype;
+
+Tween.propHooks = {
+	_default: {
+		get: function( tween ) {
+			var result;
+
+			if ( tween.elem[ tween.prop ] != null &&
+				(!tween.elem.style || tween.elem.style[ tween.prop ] == null) ) {
+				return tween.elem[ tween.prop ];
+			}
+
+			// passing an empty string as a 3rd parameter to .css will automatically
+			// attempt a parseFloat and fallback to a string if the parse fails
+			// so, simple values such as "10px" are parsed to Float.
+			// complex values such as "rotate(1rad)" are returned as is.
+			result = jQuery.css( tween.elem, tween.prop, "" );
+			// Empty strings, null, undefined and "auto" are converted to 0.
+			return !result || result === "auto" ? 0 : result;
+		},
+		set: function( tween ) {
+			// use step hook for back compat - use cssHook if its there - use .style if its
+			// available and use plain properties where available
+			if ( jQuery.fx.step[ tween.prop ] ) {
+				jQuery.fx.step[ tween.prop ]( tween );
+			} else if ( tween.elem.style && ( tween.elem.style[ jQuery.cssProps[ tween.prop ] ] != null || jQuery.cssHooks[ tween.prop ] ) ) {
+				jQuery.style( tween.elem, tween.prop, tween.now + tween.unit );
+			} else {
+				tween.elem[ tween.prop ] = tween.now;
+			}
+		}
+	}
+};
+
+// Support: IE9
+// Panic based approach to setting things on disconnected nodes
+
+Tween.propHooks.scrollTop = Tween.propHooks.scrollLeft = {
+	set: function( tween ) {
+		if ( tween.elem.nodeType && tween.elem.parentNode ) {
+			tween.elem[ tween.prop ] = tween.now;
+		}
+	}
+};
+
+jQuery.easing = {
+	linear: function( p ) {
+		return p;
+	},
+	swing: function( p ) {
+		return 0.5 - Math.cos( p * Math.PI ) / 2;
+	}
+};
+
+jQuery.fx = Tween.prototype.init;
+
+// Back Compat <1.8 extension point
+jQuery.fx.step = {};
+
+
+
+
+var
+	fxNow, timerId,
+	rfxtypes = /^(?:toggle|show|hide)$/,
+	rfxnum = new RegExp( "^(?:([+-])=|)(" + pnum + ")([a-z%]*)$", "i" ),
+	rrun = /queueHooks$/,
+	animationPrefilters = [ defaultPrefilter ],
+	tweeners = {
+		"*": [ function( prop, value ) {
+			var tween = this.createTween( prop, value ),
+				target = tween.cur(),
+				parts = rfxnum.exec( value ),
+				unit = parts && parts[ 3 ] || ( jQuery.cssNumber[ prop ] ? "" : "px" ),
+
+				// Starting value computation is required for potential unit mismatches
+				start = ( jQuery.cssNumber[ prop ] || unit !== "px" && +target ) &&
+					rfxnum.exec( jQuery.css( tween.elem, prop ) ),
+				scale = 1,
+				maxIterations = 20;
+
+			if ( start && start[ 3 ] !== unit ) {
+				// Trust units reported by jQuery.css
+				unit = unit || start[ 3 ];
+
+				// Make sure we update the tween properties later on
+				parts = parts || [];
+
+				// Iteratively approximate from a nonzero starting point
+				start = +target || 1;
+
+				do {
+					// If previous iteration zeroed out, double until we get *something*
+					// Use a string for doubling factor so we don't accidentally see scale as unchanged below
+					scale = scale || ".5";
+
+					// Adjust and apply
+					start = start / scale;
+					jQuery.style( tween.elem, prop, start + unit );
+
+				// Update scale, tolerating zero or NaN from tween.cur()
+				// And breaking the loop if scale is unchanged or perfect, or if we've just had enough
+				} while ( scale !== (scale = tween.cur() / target) && scale !== 1 && --maxIterations );
+			}
+
+			// Update tween properties
+			if ( parts ) {
+				start = tween.start = +start || +target || 0;
+				tween.unit = unit;
+				// If a +=/-= token was provided, we're doing a relative animation
+				tween.end = parts[ 1 ] ?
+					start + ( parts[ 1 ] + 1 ) * parts[ 2 ] :
+					+parts[ 2 ];
+			}
+
+			return tween;
+		} ]
+	};
+
+// Animations created synchronously will run synchronously
+function createFxNow() {
+	setTimeout(function() {
+		fxNow = undefined;
+	});
+	return ( fxNow = jQuery.now() );
+}
+
+// Generate parameters to create a standard animation
+function genFx( type, includeWidth ) {
+	var which,
+		i = 0,
+		attrs = { height: type };
+
+	// if we include width, step value is 1 to do all cssExpand values,
+	// if we don't include width, step value is 2 to skip over Left and Right
+	includeWidth = includeWidth ? 1 : 0;
+	for ( ; i < 4 ; i += 2 - includeWidth ) {
+		which = cssExpand[ i ];
+		attrs[ "margin" + which ] = attrs[ "padding" + which ] = type;
+	}
+
+	if ( includeWidth ) {
+		attrs.opacity = attrs.width = type;
+	}
+
+	return attrs;
+}
+
+function createTween( value, prop, animation ) {
+	var tween,
+		collection = ( tweeners[ prop ] || [] ).concat( tweeners[ "*" ] ),
+		index = 0,
+		length = collection.length;
+	for ( ; index < length; index++ ) {
+		if ( (tween = collection[ index ].call( animation, prop, value )) ) {
+
+			// we're done with this property
+			return tween;
+		}
+	}
+}
+
+function defaultPrefilter( elem, props, opts ) {
+	/* jshint validthis: true */
+	var prop, value, toggle, tween, hooks, oldfire, display, checkDisplay,
+		anim = this,
+		orig = {},
+		style = elem.style,
+		hidden = elem.nodeType && isHidden( elem ),
+		dataShow = data_priv.get( elem, "fxshow" );
+
+	// handle queue: false promises
+	if ( !opts.queue ) {
+		hooks = jQuery._queueHooks( elem, "fx" );
+		if ( hooks.unqueued == null ) {
+			hooks.unqueued = 0;
+			oldfire = hooks.empty.fire;
+			hooks.empty.fire = function() {
+				if ( !hooks.unqueued ) {
+					oldfire();
+				}
+			};
+		}
+		hooks.unqueued++;
+
+		anim.always(function() {
+			// doing this makes sure that the complete handler will be called
+			// before this completes
+			anim.always(function() {
+				hooks.unqueued--;
+				if ( !jQuery.queue( elem, "fx" ).length ) {
+					hooks.empty.fire();
+				}
+			});
+		});
+	}
+
+	// height/width overflow pass
+	if ( elem.nodeType === 1 && ( "height" in props || "width" in props ) ) {
+		// Make sure that nothing sneaks out
+		// Record all 3 overflow attributes because IE9-10 do not
+		// change the overflow attribute when overflowX and
+		// overflowY are set to the same value
+		opts.overflow = [ style.overflow, style.overflowX, style.overflowY ];
+
+		// Set display property to inline-block for height/width
+		// animations on inline elements that are having width/height animated
+		display = jQuery.css( elem, "display" );
+
+		// Test default display if display is currently "none"
+		checkDisplay = display === "none" ?
+			data_priv.get( elem, "olddisplay" ) || defaultDisplay( elem.nodeName ) : display;
+
+		if ( checkDisplay === "inline" && jQuery.css( elem, "float" ) === "none" ) {
+			style.display = "inline-block";
+		}
+	}
+
+	if ( opts.overflow ) {
+		style.overflow = "hidden";
+		anim.always(function() {
+			style.overflow = opts.overflow[ 0 ];
+			style.overflowX = opts.overflow[ 1 ];
+			style.overflowY = opts.overflow[ 2 ];
+		});
+	}
+
+	// show/hide pass
+	for ( prop in props ) {
+		value = props[ prop ];
+		if ( rfxtypes.exec( value ) ) {
+			delete props[ prop ];
+			toggle = toggle || value === "toggle";
+			if ( value === ( hidden ? "hide" : "show" ) ) {
+
+				// If there is dataShow left over from a stopped hide or show and we are going to proceed with show, we should pretend to be hidden
+				if ( value === "show" && dataShow && dataShow[ prop ] !== undefined ) {
+					hidden = true;
+				} else {
+					continue;
+				}
+			}
+			orig[ prop ] = dataShow && dataShow[ prop ] || jQuery.style( elem, prop );
+
+		// Any non-fx value stops us from restoring the original display value
+		} else {
+			display = undefined;
+		}
+	}
+
+	if ( !jQuery.isEmptyObject( orig ) ) {
+		if ( dataShow ) {
+			if ( "hidden" in dataShow ) {
+				hidden = dataShow.hidden;
+			}
+		} else {
+			dataShow = data_priv.access( elem, "fxshow", {} );
+		}
+
+		// store state if its toggle - enables .stop().toggle() to "reverse"
+		if ( toggle ) {
+			dataShow.hidden = !hidden;
+		}
+		if ( hidden ) {
+			jQuery( elem ).show();
+		} else {
+			anim.done(function() {
+				jQuery( elem ).hide();
+			});
+		}
+		anim.done(function() {
+			var prop;
+
+			data_priv.remove( elem, "fxshow" );
+			for ( prop in orig ) {
+				jQuery.style( elem, prop, orig[ prop ] );
+			}
+		});
+		for ( prop in orig ) {
+			tween = createTween( hidden ? dataShow[ prop ] : 0, prop, anim );
+
+			if ( !( prop in dataShow ) ) {
+				dataShow[ prop ] = tween.start;
+				if ( hidden ) {
+					tween.end = tween.start;
+					tween.start = prop === "width" || prop === "height" ? 1 : 0;
+				}
+			}
+		}
+
+	// If this is a noop like .hide().hide(), restore an overwritten display value
+	} else if ( (display === "none" ? defaultDisplay( elem.nodeName ) : display) === "inline" ) {
+		style.display = display;
+	}
+}
+
+function propFilter( props, specialEasing ) {
+	var index, name, easing, value, hooks;
+
+	// camelCase, specialEasing and expand cssHook pass
+	for ( index in props ) {
+		name = jQuery.camelCase( index );
+		easing = specialEasing[ name ];
+		value = props[ index ];
+		if ( jQuery.isArray( value ) ) {
+			easing = value[ 1 ];
+			value = props[ index ] = value[ 0 ];
+		}
+
+		if ( index !== name ) {
+			props[ name ] = value;
+			delete props[ index ];
+		}
+
+		hooks = jQuery.cssHooks[ name ];
+		if ( hooks && "expand" in hooks ) {
+			value = hooks.expand( value );
+			delete props[ name ];
+
+			// not quite $.extend, this wont overwrite keys already present.
+			// also - reusing 'index' from above because we have the correct "name"
+			for ( index in value ) {
+				if ( !( index in props ) ) {
+					props[ index ] = value[ index ];
+					specialEasing[ index ] = easing;
+				}
+			}
+		} else {
+			specialEasing[ name ] = easing;
+		}
+	}
+}
+
+function Animation( elem, properties, options ) {
+	var result,
+		stopped,
+		index = 0,
+		length = animationPrefilters.length,
+		deferred = jQuery.Deferred().always( function() {
+			// don't match elem in the :animated selector
+			delete tick.elem;
+		}),
+		tick = function() {
+			if ( stopped ) {
+				return false;
+			}
+			var currentTime = fxNow || createFxNow(),
+				remaining = Math.max( 0, animation.startTime + animation.duration - currentTime ),
+				// archaic crash bug won't allow us to use 1 - ( 0.5 || 0 ) (#12497)
+				temp = remaining / animation.duration || 0,
+				percent = 1 - temp,
+				index = 0,
+				length = animation.tweens.length;
+
+			for ( ; index < length ; index++ ) {
+				animation.tweens[ index ].run( percent );
+			}
+
+			deferred.notifyWith( elem, [ animation, percent, remaining ]);
+
+			if ( percent < 1 && length ) {
+				return remaining;
+			} else {
+				deferred.resolveWith( elem, [ animation ] );
+				return false;
+			}
+		},
+		animation = deferred.promise({
+			elem: elem,
+			props: jQuery.extend( {}, properties ),
+			opts: jQuery.extend( true, { specialEasing: {} }, options ),
+			originalProperties: properties,
+			originalOptions: options,
+			startTime: fxNow || createFxNow(),
+			duration: options.duration,
+			tweens: [],
+			createTween: function( prop, end ) {
+				var tween = jQuery.Tween( elem, animation.opts, prop, end,
+						animation.opts.specialEasing[ prop ] || animation.opts.easing );
+				animation.tweens.push( tween );
+				return tween;
+			},
+			stop: function( gotoEnd ) {
+				var index = 0,
+					// if we are going to the end, we want to run all the tweens
+					// otherwise we skip this part
+					length = gotoEnd ? animation.tweens.length : 0;
+				if ( stopped ) {
+					return this;
+				}
+				stopped = true;
+				for ( ; index < length ; index++ ) {
+					animation.tweens[ index ].run( 1 );
+				}
+
+				// resolve when we played the last frame
+				// otherwise, reject
+				if ( gotoEnd ) {
+					deferred.resolveWith( elem, [ animation, gotoEnd ] );
+				} else {
+					deferred.rejectWith( elem, [ animation, gotoEnd ] );
+				}
+				return this;
+			}
+		}),
+		props = animation.props;
+
+	propFilter( props, animation.opts.specialEasing );
+
+	for ( ; index < length ; index++ ) {
+		result = animationPrefilters[ index ].call( animation, elem, props, animation.opts );
+		if ( result ) {
+			return result;
+		}
+	}
+
+	jQuery.map( props, createTween, animation );
+
+	if ( jQuery.isFunction( animation.opts.start ) ) {
+		animation.opts.start.call( elem, animation );
+	}
+
+	jQuery.fx.timer(
+		jQuery.extend( tick, {
+			elem: elem,
+			anim: animation,
+			queue: animation.opts.queue
+		})
+	);
+
+	// attach callbacks from options
+	return animation.progress( animation.opts.progress )
+		.done( animation.opts.done, animation.opts.complete )
+		.fail( animation.opts.fail )
+		.always( animation.opts.always );
+}
+
+jQuery.Animation = jQuery.extend( Animation, {
+
+	tweener: function( props, callback ) {
+		if ( jQuery.isFunction( props ) ) {
+			callback = props;
+			props = [ "*" ];
+		} else {
+			props = props.split(" ");
+		}
+
+		var prop,
+			index = 0,
+			length = props.length;
+
+		for ( ; index < length ; index++ ) {
+			prop = props[ index ];
+			tweeners[ prop ] = tweeners[ prop ] || [];
+			tweeners[ prop ].unshift( callback );
+		}
+	},
+
+	prefilter: function( callback, prepend ) {
+		if ( prepend ) {
+			animationPrefilters.unshift( callback );
+		} else {
+			animationPrefilters.push( callback );
+		}
+	}
+});
+
+jQuery.speed = function( speed, easing, fn ) {
+	var opt = speed && typeof speed === "object" ? jQuery.extend( {}, speed ) : {
+		complete: fn || !fn && easing ||
+			jQuery.isFunction( speed ) && speed,
+		duration: speed,
+		easing: fn && easing || easing && !jQuery.isFunction( easing ) && easing
+	};
+
+	opt.duration = jQuery.fx.off ? 0 : typeof opt.duration === "number" ? opt.duration :
+		opt.duration in jQuery.fx.speeds ? jQuery.fx.speeds[ opt.duration ] : jQuery.fx.speeds._default;
+
+	// normalize opt.queue - true/undefined/null -> "fx"
+	if ( opt.queue == null || opt.queue === true ) {
+		opt.queue = "fx";
+	}
+
+	// Queueing
+	opt.old = opt.complete;
+
+	opt.complete = function() {
+		if ( jQuery.isFunction( opt.old ) ) {
+			opt.old.call( this );
+		}
+
+		if ( opt.queue ) {
+			jQuery.dequeue( this, opt.queue );
+		}
+	};
+
+	return opt;
+};
+
+jQuery.fn.extend({
+	fadeTo: function( speed, to, easing, callback ) {
+
+		// show any hidden elements after setting opacity to 0
+		return this.filter( isHidden ).css( "opacity", 0 ).show()
+
+			// animate to the value specified
+			.end().animate({ opacity: to }, speed, easing, callback );
+	},
+	animate: function( prop, speed, easing, callback ) {
+		var empty = jQuery.isEmptyObject( prop ),
+			optall = jQuery.speed( speed, easing, callback ),
+			doAnimation = function() {
+				// Operate on a copy of prop so per-property easing won't be lost
+				var anim = Animation( this, jQuery.extend( {}, prop ), optall );
+
+				// Empty animations, or finishing resolves immediately
+				if ( empty || data_priv.get( this, "finish" ) ) {
+					anim.stop( true );
+				}
+			};
+			doAnimation.finish = doAnimation;
+
+		return empty || optall.queue === false ?
+			this.each( doAnimation ) :
+			this.queue( optall.queue, doAnimation );
+	},
+	stop: function( type, clearQueue, gotoEnd ) {
+		var stopQueue = function( hooks ) {
+			var stop = hooks.stop;
+			delete hooks.stop;
+			stop( gotoEnd );
+		};
+
+		if ( typeof type !== "string" ) {
+			gotoEnd = clearQueue;
+			clearQueue = type;
+			type = undefined;
+		}
+		if ( clearQueue && type !== false ) {
+			this.queue( type || "fx", [] );
+		}
+
+		return this.each(function() {
+			var dequeue = true,
+				index = type != null && type + "queueHooks",
+				timers = jQuery.timers,
+				data = data_priv.get( this );
+
+			if ( index ) {
+				if ( data[ index ] && data[ index ].stop ) {
+					stopQueue( data[ index ] );
+				}
+			} else {
+				for ( index in data ) {
+					if ( data[ index ] && data[ index ].stop && rrun.test( index ) ) {
+						stopQueue( data[ index ] );
+					}
+				}
+			}
+
+			for ( index = timers.length; index--; ) {
+				if ( timers[ index ].elem === this && (type == null || timers[ index ].queue === type) ) {
+					timers[ index ].anim.stop( gotoEnd );
+					dequeue = false;
+					timers.splice( index, 1 );
+				}
+			}
+
+			// start the next in the queue if the last step wasn't forced
+			// timers currently will call their complete callbacks, which will dequeue
+			// but only if they were gotoEnd
+			if ( dequeue || !gotoEnd ) {
+				jQuery.dequeue( this, type );
+			}
+		});
+	},
+	finish: function( type ) {
+		if ( type !== false ) {
+			type = type || "fx";
+		}
+		return this.each(function() {
+			var index,
+				data = data_priv.get( this ),
+				queue = data[ type + "queue" ],
+				hooks = data[ type + "queueHooks" ],
+				timers = jQuery.timers,
+				length = queue ? queue.length : 0;
+
+			// enable finishing flag on private data
+			data.finish = true;
+
+			// empty the queue first
+			jQuery.queue( this, type, [] );
+
+			if ( hooks && hooks.stop ) {
+				hooks.stop.call( this, true );
+			}
+
+			// look for any active animations, and finish them
+			for ( index = timers.length; index--; ) {
+				if ( timers[ index ].elem === this && timers[ index ].queue === type ) {
+					timers[ index ].anim.stop( true );
+					timers.splice( index, 1 );
+				}
+			}
+
+			// look for any animations in the old queue and finish them
+			for ( index = 0; index < length; index++ ) {
+				if ( queue[ index ] && queue[ index ].finish ) {
+					queue[ index ].finish.call( this );
+				}
+			}
+
+			// turn off finishing flag
+			delete data.finish;
+		});
+	}
+});
+
+jQuery.each([ "toggle", "show", "hide" ], function( i, name ) {
+	var cssFn = jQuery.fn[ name ];
+	jQuery.fn[ name ] = function( speed, easing, callback ) {
+		return speed == null || typeof speed === "boolean" ?
+			cssFn.apply( this, arguments ) :
+			this.animate( genFx( name, true ), speed, easing, callback );
+	};
+});
+
+// Generate shortcuts for custom animations
+jQuery.each({
+	slideDown: genFx("show"),
+	slideUp: genFx("hide"),
+	slideToggle: genFx("toggle"),
+	fadeIn: { opacity: "show" },
+	fadeOut: { opacity: "hide" },
+	fadeToggle: { opacity: "toggle" }
+}, function( name, props ) {
+	jQuery.fn[ name ] = function( speed, easing, callback ) {
+		return this.animate( props, speed, easing, callback );
+	};
+});
+
+jQuery.timers = [];
+jQuery.fx.tick = function() {
+	var timer,
+		i = 0,
+		timers = jQuery.timers;
+
+	fxNow = jQuery.now();
+
+	for ( ; i < timers.length; i++ ) {
+		timer = timers[ i ];
+		// Checks the timer has not already been removed
+		if ( !timer() && timers[ i ] === timer ) {
+			timers.splice( i--, 1 );
+		}
+	}
+
+	if ( !timers.length ) {
+		jQuery.fx.stop();
+	}
+	fxNow = undefined;
+};
+
+jQuery.fx.timer = function( timer ) {
+	jQuery.timers.push( timer );
+	if ( timer() ) {
+		jQuery.fx.start();
+	} else {
+		jQuery.timers.pop();
+	}
+};
+
+jQuery.fx.interval = 13;
+
+jQuery.fx.start = function() {
+	if ( !timerId ) {
+		timerId = setInterval( jQuery.fx.tick, jQuery.fx.interval );
+	}
+};
+
+jQuery.fx.stop = function() {
+	clearInterval( timerId );
+	timerId = null;
+};
+
+jQuery.fx.speeds = {
+	slow: 600,
+	fast: 200,
+	// Default speed
+	_default: 400
+};
+
+
+// Based off of the plugin by Clint Helfers, with permission.
+// http://blindsignals.com/index.php/2009/07/jquery-delay/
+jQuery.fn.delay = function( time, type ) {
+	time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time;
+	type = type || "fx";
+
+	return this.queue( type, function( next, hooks ) {
+		var timeout = setTimeout( next, time );
+		hooks.stop = function() {
+			clearTimeout( timeout );
+		};
+	});
+};
+
+
+(function() {
+	var input = document.createElement( "input" ),
+		select = document.createElement( "select" ),
+		opt = select.appendChild( document.createElement( "option" ) );
+
+	input.type = "checkbox";
+
+	// Support: iOS 5.1, Android 4.x, Android 2.3
+	// Check the default checkbox/radio value ("" on old WebKit; "on" elsewhere)
+	support.checkOn = input.value !== "";
+
+	// Must access the parent to make an option select properly
+	// Support: IE9, IE10
+	support.optSelected = opt.selected;
+
+	// Make sure that the options inside disabled selects aren't marked as disabled
+	// (WebKit marks them as disabled)
+	select.disabled = true;
+	support.optDisabled = !opt.disabled;
+
+	// Check if an input maintains its value after becoming a radio
+	// Support: IE9, IE10
+	input = document.createElement( "input" );
+	input.value = "t";
+	input.type = "radio";
+	support.radioValue = input.value === "t";
+})();
+
+
+var nodeHook, boolHook,
+	attrHandle = jQuery.expr.attrHandle;
+
+jQuery.fn.extend({
+	attr: function( name, value ) {
+		return access( this, jQuery.attr, name, value, arguments.length > 1 );
+	},
+
+	removeAttr: function( name ) {
+		return this.each(function() {
+			jQuery.removeAttr( this, name );
+		});
+	}
+});
+
+jQuery.extend({
+	attr: function( elem, name, value ) {
+		var hooks, ret,
+			nType = elem.nodeType;
+
+		// don't get/set attributes on text, comment and attribute nodes
+		if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
+			return;
+		}
+
+		// Fallback to prop when attributes are not supported
+		if ( typeof elem.getAttribute === strundefined ) {
+			return jQuery.prop( elem, name, value );
+		}
+
+		// All attributes are lowercase
+		// Grab necessary hook if one is defined
+		if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) {
+			name = name.toLowerCase();
+			hooks = jQuery.attrHooks[ name ] ||
+				( jQuery.expr.match.bool.test( name ) ? boolHook : nodeHook );
+		}
+
+		if ( value !== undefined ) {
+
+			if ( value === null ) {
+				jQuery.removeAttr( elem, name );
+
+			} else if ( hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) {
+				return ret;
+
+			} else {
+				elem.setAttribute( name, value + "" );
+				return value;
+			}
+
+		} else if ( hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) {
+			return ret;
+
+		} else {
+			ret = jQuery.find.attr( elem, name );
+
+			// Non-existent attributes return null, we normalize to undefined
+			return ret == null ?
+				undefined :
+				ret;
+		}
+	},
+
+	removeAttr: function( elem, value ) {
+		var name, propName,
+			i = 0,
+			attrNames = value && value.match( rnotwhite );
+
+		if ( attrNames && elem.nodeType === 1 ) {
+			while ( (name = attrNames[i++]) ) {
+				propName = jQuery.propFix[ name ] || name;
+
+				// Boolean attributes get special treatment (#10870)
+				if ( jQuery.expr.match.bool.test( name ) ) {
+					// Set corresponding property to false
+					elem[ propName ] = false;
+				}
+
+				elem.removeAttribute( name );
+			}
+		}
+	},
+
+	attrHooks: {
+		type: {
+			set: function( elem, value ) {
+				if ( !support.radioValue && value === "radio" &&
+					jQuery.nodeName( elem, "input" ) ) {
+					// Setting the type on a radio button after the value resets the value in IE6-9
+					// Reset value to default in case type is set after value during creation
+					var val = elem.value;
+					elem.setAttribute( "type", value );
+					if ( val ) {
+						elem.value = val;
+					}
+					return value;
+				}
+			}
+		}
+	}
+});
+
+// Hooks for boolean attributes
+boolHook = {
+	set: function( elem, value, name ) {
+		if ( value === false ) {
+			// Remove boolean attributes when set to false
+			jQuery.removeAttr( elem, name );
+		} else {
+			elem.setAttribute( name, name );
+		}
+		return name;
+	}
+};
+jQuery.each( jQuery.expr.match.bool.source.match( /\w+/g ), function( i, name ) {
+	var getter = attrHandle[ name ] || jQuery.find.attr;
+
+	attrHandle[ name ] = function( elem, name, isXML ) {
+		var ret, handle;
+		if ( !isXML ) {
+			// Avoid an infinite loop by temporarily removing this function from the getter
+			handle = attrHandle[ name ];
+			attrHandle[ name ] = ret;
+			ret = getter( elem, name, isXML ) != null ?
+				name.toLowerCase() :
+				null;
+			attrHandle[ name ] = handle;
+		}
+		return ret;
+	};
+});
+
+
+
+
+var rfocusable = /^(?:input|select|textarea|button)$/i;
+
+jQuery.fn.extend({
+	prop: function( name, value ) {
+		return access( this, jQuery.prop, name, value, arguments.length > 1 );
+	},
+
+	removeProp: function( name ) {
+		return this.each(function() {
+			delete this[ jQuery.propFix[ name ] || name ];
+		});
+	}
+});
+
+jQuery.extend({
+	propFix: {
+		"for": "htmlFor",
+		"class": "className"
+	},
+
+	prop: function( elem, name, value ) {
+		var ret, hooks, notxml,
+			nType = elem.nodeType;
+
+		// don't get/set properties on text, comment and attribute nodes
+		if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
+			return;
+		}
+
+		notxml = nType !== 1 || !jQuery.isXMLDoc( elem );
+
+		if ( notxml ) {
+			// Fix name and attach hooks
+			name = jQuery.propFix[ name ] || name;
+			hooks = jQuery.propHooks[ name ];
+		}
+
+		if ( value !== undefined ) {
+			return hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ?
+				ret :
+				( elem[ name ] = value );
+
+		} else {
+			return hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ?
+				ret :
+				elem[ name ];
+		}
+	},
+
+	propHooks: {
+		tabIndex: {
+			get: function( elem ) {
+				return elem.hasAttribute( "tabindex" ) || rfocusable.test( elem.nodeName ) || elem.href ?
+					elem.tabIndex :
+					-1;
+			}
+		}
+	}
+});
+
+// Support: IE9+
+// Selectedness for an option in an optgroup can be inaccurate
+if ( !support.optSelected ) {
+	jQuery.propHooks.selected = {
+		get: function( elem ) {
+			var parent = elem.parentNode;
+			if ( parent && parent.parentNode ) {
+				parent.parentNode.selectedIndex;
+			}
+			return null;
+		}
+	};
+}
+
+jQuery.each([
+	"tabIndex",
+	"readOnly",
+	"maxLength",
+	"cellSpacing",
+	"cellPadding",
+	"rowSpan",
+	"colSpan",
+	"useMap",
+	"frameBorder",
+	"contentEditable"
+], function() {
+	jQuery.propFix[ this.toLowerCase() ] = this;
+});
+
+
+
+
+var rclass = /[\t\r\n\f]/g;
+
+jQuery.fn.extend({
+	addClass: function( value ) {
+		var classes, elem, cur, clazz, j, finalValue,
+			proceed = typeof value === "string" && value,
+			i = 0,
+			len = this.length;
+
+		if ( jQuery.isFunction( value ) ) {
+			return this.each(function( j ) {
+				jQuery( this ).addClass( value.call( this, j, this.className ) );
+			});
+		}
+
+		if ( proceed ) {
+			// The disjunction here is for better compressibility (see removeClass)
+			classes = ( value || "" ).match( rnotwhite ) || [];
+
+			for ( ; i < len; i++ ) {
+				elem = this[ i ];
+				cur = elem.nodeType === 1 && ( elem.className ?
+					( " " + elem.className + " " ).replace( rclass, " " ) :
+					" "
+				);
+
+				if ( cur ) {
+					j = 0;
+					while ( (clazz = classes[j++]) ) {
+						if ( cur.indexOf( " " + clazz + " " ) < 0 ) {
+							cur += clazz + " ";
+						}
+					}
+
+					// only assign if different to avoid unneeded rendering.
+					finalValue = jQuery.trim( cur );
+					if ( elem.className !== finalValue ) {
+						elem.className = finalValue;
+					}
+				}
+			}
+		}
+
+		return this;
+	},
+
+	removeClass: function( value ) {
+		var classes, elem, cur, clazz, j, finalValue,
+			proceed = arguments.length === 0 || typeof value === "string" && value,
+			i = 0,
+			len = this.length;
+
+		if ( jQuery.isFunction( value ) ) {
+			return this.each(function( j ) {
+				jQuery( this ).removeClass( value.call( this, j, this.className ) );
+			});
+		}
+		if ( proceed ) {
+			classes = ( value || "" ).match( rnotwhite ) || [];
+
+			for ( ; i < len; i++ ) {
+				elem = this[ i ];
+				// This expression is here for better compressibility (see addClass)
+				cur = elem.nodeType === 1 && ( elem.className ?
+					( " " + elem.className + " " ).replace( rclass, " " ) :
+					""
+				);
+
+				if ( cur ) {
+					j = 0;
+					while ( (clazz = classes[j++]) ) {
+						// Remove *all* instances
+						while ( cur.indexOf( " " + clazz + " " ) >= 0 ) {
+							cur = cur.replace( " " + clazz + " ", " " );
+						}
+					}
+
+					// only assign if different to avoid unneeded rendering.
+					finalValue = value ? jQuery.trim( cur ) : "";
+					if ( elem.className !== finalValue ) {
+						elem.className = finalValue;
+					}
+				}
+			}
+		}
+
+		return this;
+	},
+
+	toggleClass: function( value, stateVal ) {
+		var type = typeof value;
+
+		if ( typeof stateVal === "boolean" && type === "string" ) {
+			return stateVal ? this.addClass( value ) : this.removeClass( value );
+		}
+
+		if ( jQuery.isFunction( value ) ) {
+			return this.each(function( i ) {
+				jQuery( this ).toggleClass( value.call(this, i, this.className, stateVal), stateVal );
+			});
+		}
+
+		return this.each(function() {
+			if ( type === "string" ) {
+				// toggle individual class names
+				var className,
+					i = 0,
+					self = jQuery( this ),
+					classNames = value.match( rnotwhite ) || [];
+
+				while ( (className = classNames[ i++ ]) ) {
+					// check each className given, space separated list
+					if ( self.hasClass( className ) ) {
+						self.removeClass( className );
+					} else {
+						self.addClass( className );
+					}
+				}
+
+			// Toggle whole class name
+			} else if ( type === strundefined || type === "boolean" ) {
+				if ( this.className ) {
+					// store className if set
+					data_priv.set( this, "__className__", this.className );
+				}
+
+				// If the element has a class name or if we're passed "false",
+				// then remove the whole classname (if there was one, the above saved it).
+				// Otherwise bring back whatever was previously saved (if anything),
+				// falling back to the empty string if nothing was stored.
+				this.className = this.className || value === false ? "" : data_priv.get( this, "__className__" ) || "";
+			}
+		});
+	},
+
+	hasClass: function( selector ) {
+		var className = " " + selector + " ",
+			i = 0,
+			l = this.length;
+		for ( ; i < l; i++ ) {
+			if ( this[i].nodeType === 1 && (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) >= 0 ) {
+				return true;
+			}
+		}
+
+		return false;
+	}
+});
+
+
+
+
+var rreturn = /\r/g;
+
+jQuery.fn.extend({
+	val: function( value ) {
+		var hooks, ret, isFunction,
+			elem = this[0];
+
+		if ( !arguments.length ) {
+			if ( elem ) {
+				hooks = jQuery.valHooks[ elem.type ] || jQuery.valHooks[ elem.nodeName.toLowerCase() ];
+
+				if ( hooks && "get" in hooks && (ret = hooks.get( elem, "value" )) !== undefined ) {
+					return ret;
+				}
+
+				ret = elem.value;
+
+				return typeof ret === "string" ?
+					// handle most common string cases
+					ret.replace(rreturn, "") :
+					// handle cases where value is null/undef or number
+					ret == null ? "" : ret;
+			}
+
+			return;
+		}
+
+		isFunction = jQuery.isFunction( value );
+
+		return this.each(function( i ) {
+			var val;
+
+			if ( this.nodeType !== 1 ) {
+				return;
+			}
+
+			if ( isFunction ) {
+				val = value.call( this, i, jQuery( this ).val() );
+			} else {
+				val = value;
+			}
+
+			// Treat null/undefined as ""; convert numbers to string
+			if ( val == null ) {
+				val = "";
+
+			} else if ( typeof val === "number" ) {
+				val += "";
+
+			} else if ( jQuery.isArray( val ) ) {
+				val = jQuery.map( val, function( value ) {
+					return value == null ? "" : value + "";
+				});
+			}
+
+			hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ];
+
+			// If set returns undefined, fall back to normal setting
+			if ( !hooks || !("set" in hooks) || hooks.set( this, val, "value" ) === undefined ) {
+				this.value = val;
+			}
+		});
+	}
+});
+
+jQuery.extend({
+	valHooks: {
+		option: {
+			get: function( elem ) {
+				var val = jQuery.find.attr( elem, "value" );
+				return val != null ?
+					val :
+					// Support: IE10-11+
+					// option.text throws exceptions (#14686, #14858)
+					jQuery.trim( jQuery.text( elem ) );
+			}
+		},
+		select: {
+			get: function( elem ) {
+				var value, option,
+					options = elem.options,
+					index = elem.selectedIndex,
+					one = elem.type === "select-one" || index < 0,
+					values = one ? null : [],
+					max = one ? index + 1 : options.length,
+					i = index < 0 ?
+						max :
+						one ? index : 0;
+
+				// Loop through all the selected options
+				for ( ; i < max; i++ ) {
+					option = options[ i ];
+
+					// IE6-9 doesn't update selected after form reset (#2551)
+					if ( ( option.selected || i === index ) &&
+							// Don't return options that are disabled or in a disabled optgroup
+							( support.optDisabled ? !option.disabled : option.getAttribute( "disabled" ) === null ) &&
+							( !option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" ) ) ) {
+
+						// Get the specific value for the option
+						value = jQuery( option ).val();
+
+						// We don't need an array for one selects
+						if ( one ) {
+							return value;
+						}
+
+						// Multi-Selects return an array
+						values.push( value );
+					}
+				}
+
+				return values;
+			},
+
+			set: function( elem, value ) {
+				var optionSet, option,
+					options = elem.options,
+					values = jQuery.makeArray( value ),
+					i = options.length;
+
+				while ( i-- ) {
+					option = options[ i ];
+					if ( (option.selected = jQuery.inArray( option.value, values ) >= 0) ) {
+						optionSet = true;
+					}
+				}
+
+				// force browsers to behave consistently when non-matching value is set
+				if ( !optionSet ) {
+					elem.selectedIndex = -1;
+				}
+				return values;
+			}
+		}
+	}
+});
+
+// Radios and checkboxes getter/setter
+jQuery.each([ "radio", "checkbox" ], function() {
+	jQuery.valHooks[ this ] = {
+		set: function( elem, value ) {
+			if ( jQuery.isArray( value ) ) {
+				return ( elem.checked = jQuery.inArray( jQuery(elem).val(), value ) >= 0 );
+			}
+		}
+	};
+	if ( !support.checkOn ) {
+		jQuery.valHooks[ this ].get = function( elem ) {
+			// Support: Webkit
+			// "" is returned instead of "on" if a value isn't specified
+			return elem.getAttribute("value") === null ? "on" : elem.value;
+		};
+	}
+});
+
+
+
+
+// Return jQuery for attributes-only inclusion
+
+
+jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " +
+	"mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +
+	"change select submit keydown keypress keyup error contextmenu").split(" "), function( i, name ) {
+
+	// Handle event binding
+	jQuery.fn[ name ] = function( data, fn ) {
+		return arguments.length > 0 ?
+			this.on( name, null, data, fn ) :
+			this.trigger( name );
+	};
+});
+
+jQuery.fn.extend({
+	hover: function( fnOver, fnOut ) {
+		return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );
+	},
+
+	bind: function( types, data, fn ) {
+		return this.on( types, null, data, fn );
+	},
+	unbind: function( types, fn ) {
+		return this.off( types, null, fn );
+	},
+
+	delegate: function( selector, types, data, fn ) {
+		return this.on( types, selector, data, fn );
+	},
+	undelegate: function( selector, types, fn ) {
+		// ( namespace ) or ( selector, types [, fn] )
+		return arguments.length === 1 ? this.off( selector, "**" ) : this.off( types, selector || "**", fn );
+	}
+});
+
+
+var nonce = jQuery.now();
+
+var rquery = (/\?/);
+
+
+
+// Support: Android 2.3
+// Workaround failure to string-cast null input
+jQuery.parseJSON = function( data ) {
+	return JSON.parse( data + "" );
+};
+
+
+// Cross-browser xml parsing
+jQuery.parseXML = function( data ) {
+	var xml, tmp;
+	if ( !data || typeof data !== "string" ) {
+		return null;
+	}
+
+	// Support: IE9
+	try {
+		tmp = new DOMParser();
+		xml = tmp.parseFromString( data, "text/xml" );
+	} catch ( e ) {
+		xml = undefined;
+	}
+
+	if ( !xml || xml.getElementsByTagName( "parsererror" ).length ) {
+		jQuery.error( "Invalid XML: " + data );
+	}
+	return xml;
+};
+
+
+var
+	// Document location
+	ajaxLocParts,
+	ajaxLocation,
+
+	rhash = /#.*$/,
+	rts = /([?&])_=[^&]*/,
+	rheaders = /^(.*?):[ \t]*([^\r\n]*)$/mg,
+	// #7653, #8125, #8152: local protocol detection
+	rlocalProtocol = /^(?:about|app|app-storage|.+-extension|file|res|widget):$/,
+	rnoContent = /^(?:GET|HEAD)$/,
+	rprotocol = /^\/\//,
+	rurl = /^([\w.+-]+:)(?:\/\/(?:[^\/?#]*@|)([^\/?#:]*)(?::(\d+)|)|)/,
+
+	/* Prefilters
+	 * 1) They are useful to introduce custom dataTypes (see ajax/jsonp.js for an example)
+	 * 2) These are called:
+	 *    - BEFORE asking for a transport
+	 *    - AFTER param serialization (s.data is a string if s.processData is true)
+	 * 3) key is the dataType
+	 * 4) the catchall symbol "*" can be used
+	 * 5) execution will start with transport dataType and THEN continue down to "*" if needed
+	 */
+	prefilters = {},
+
+	/* Transports bindings
+	 * 1) key is the dataType
+	 * 2) the catchall symbol "*" can be used
+	 * 3) selection will start with transport dataType and THEN go to "*" if needed
+	 */
+	transports = {},
+
+	// Avoid comment-prolog char sequence (#10098); must appease lint and evade compression
+	allTypes = "*/".concat("*");
+
+// #8138, IE may throw an exception when accessing
+// a field from window.location if document.domain has been set
+try {
+	ajaxLocation = location.href;
+} catch( e ) {
+	// Use the href attribute of an A element
+	// since IE will modify it given document.location
+	ajaxLocation = document.createElement( "a" );
+	ajaxLocation.href = "";
+	ajaxLocation = ajaxLocation.href;
+}
+
+// Segment location into parts
+ajaxLocParts = rurl.exec( ajaxLocation.toLowerCase() ) || [];
+
+// Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport
+function addToPrefiltersOrTransports( structure ) {
+
+	// dataTypeExpression is optional and defaults to "*"
+	return function( dataTypeExpression, func ) {
+
+		if ( typeof dataTypeExpression !== "string" ) {
+			func = dataTypeExpression;
+			dataTypeExpression = "*";
+		}
+
+		var dataType,
+			i = 0,
+			dataTypes = dataTypeExpression.toLowerCase().match( rnotwhite ) || [];
+
+		if ( jQuery.isFunction( func ) ) {
+			// For each dataType in the dataTypeExpression
+			while ( (dataType = dataTypes[i++]) ) {
+				// Prepend if requested
+				if ( dataType[0] === "+" ) {
+					dataType = dataType.slice( 1 ) || "*";
+					(structure[ dataType ] = structure[ dataType ] || []).unshift( func );
+
+				// Otherwise append
+				} else {
+					(structure[ dataType ] = structure[ dataType ] || []).push( func );
+				}
+			}
+		}
+	};
+}
+
+// Base inspection function for prefilters and transports
+function inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR ) {
+
+	var inspected = {},
+		seekingTransport = ( structure === transports );
+
+	function inspect( dataType ) {
+		var selected;
+		inspected[ dataType ] = true;
+		jQuery.each( structure[ dataType ] || [], function( _, prefilterOrFactory ) {
+			var dataTypeOrTransport = prefilterOrFactory( options, originalOptions, jqXHR );
+			if ( typeof dataTypeOrTransport === "string" && !seekingTransport && !inspected[ dataTypeOrTransport ] ) {
+				options.dataTypes.unshift( dataTypeOrTransport );
+				inspect( dataTypeOrTransport );
+				return false;
+			} else if ( seekingTransport ) {
+				return !( selected = dataTypeOrTransport );
+			}
+		});
+		return selected;
+	}
+
+	return inspect( options.dataTypes[ 0 ] ) || !inspected[ "*" ] && inspect( "*" );
+}
+
+// A special extend for ajax options
+// that takes "flat" options (not to be deep extended)
+// Fixes #9887
+function ajaxExtend( target, src ) {
+	var key, deep,
+		flatOptions = jQuery.ajaxSettings.flatOptions || {};
+
+	for ( key in src ) {
+		if ( src[ key ] !== undefined ) {
+			( flatOptions[ key ] ? target : ( deep || (deep = {}) ) )[ key ] = src[ key ];
+		}
+	}
+	if ( deep ) {
+		jQuery.extend( true, target, deep );
+	}
+
+	return target;
+}
+
+/* Handles responses to an ajax request:
+ * - finds the right dataType (mediates between content-type and expected dataType)
+ * - returns the corresponding response
+ */
+function ajaxHandleResponses( s, jqXHR, responses ) {
+
+	var ct, type, finalDataType, firstDataType,
+		contents = s.contents,
+		dataTypes = s.dataTypes;
+
+	// Remove auto dataType and get content-type in the process
+	while ( dataTypes[ 0 ] === "*" ) {
+		dataTypes.shift();
+		if ( ct === undefined ) {
+			ct = s.mimeType || jqXHR.getResponseHeader("Content-Type");
+		}
+	}
+
+	// Check if we're dealing with a known content-type
+	if ( ct ) {
+		for ( type in contents ) {
+			if ( contents[ type ] && contents[ type ].test( ct ) ) {
+				dataTypes.unshift( type );
+				break;
+			}
+		}
+	}
+
+	// Check to see if we have a response for the expected dataType
+	if ( dataTypes[ 0 ] in responses ) {
+		finalDataType = dataTypes[ 0 ];
+	} else {
+		// Try convertible dataTypes
+		for ( type in responses ) {
+			if ( !dataTypes[ 0 ] || s.converters[ type + " " + dataTypes[0] ] ) {
+				finalDataType = type;
+				break;
+			}
+			if ( !firstDataType ) {
+				firstDataType = type;
+			}
+		}
+		// Or just use first one
+		finalDataType = finalDataType || firstDataType;
+	}
+
+	// If we found a dataType
+	// We add the dataType to the list if needed
+	// and return the corresponding response
+	if ( finalDataType ) {
+		if ( finalDataType !== dataTypes[ 0 ] ) {
+			dataTypes.unshift( finalDataType );
+		}
+		return responses[ finalDataType ];
+	}
+}
+
+/* Chain conversions given the request and the original response
+ * Also sets the responseXXX fields on the jqXHR instance
+ */
+function ajaxConvert( s, response, jqXHR, isSuccess ) {
+	var conv2, current, conv, tmp, prev,
+		converters = {},
+		// Work with a copy of dataTypes in case we need to modify it for conversion
+		dataTypes = s.dataTypes.slice();
+
+	// Create converters map with lowercased keys
+	if ( dataTypes[ 1 ] ) {
+		for ( conv in s.converters ) {
+			converters[ conv.toLowerCase() ] = s.converters[ conv ];
+		}
+	}
+
+	current = dataTypes.shift();
+
+	// Convert to each sequential dataType
+	while ( current ) {
+
+		if ( s.responseFields[ current ] ) {
+			jqXHR[ s.responseFields[ current ] ] = response;
+		}
+
+		// Apply the dataFilter if provided
+		if ( !prev && isSuccess && s.dataFilter ) {
+			response = s.dataFilter( response, s.dataType );
+		}
+
+		prev = current;
+		current = dataTypes.shift();
+
+		if ( current ) {
+
+		// There's only work to do if current dataType is non-auto
+			if ( current === "*" ) {
+
+				current = prev;
+
+			// Convert response if prev dataType is non-auto and differs from current
+			} else if ( prev !== "*" && prev !== current ) {
+
+				// Seek a direct converter
+				conv = converters[ prev + " " + current ] || converters[ "* " + current ];
+
+				// If none found, seek a pair
+				if ( !conv ) {
+					for ( conv2 in converters ) {
+
+						// If conv2 outputs current
+						tmp = conv2.split( " " );
+						if ( tmp[ 1 ] === current ) {
+
+							// If prev can be converted to accepted input
+							conv = converters[ prev + " " + tmp[ 0 ] ] ||
+								converters[ "* " + tmp[ 0 ] ];
+							if ( conv ) {
+								// Condense equivalence converters
+								if ( conv === true ) {
+									conv = converters[ conv2 ];
+
+								// Otherwise, insert the intermediate dataType
+								} else if ( converters[ conv2 ] !== true ) {
+									current = tmp[ 0 ];
+									dataTypes.unshift( tmp[ 1 ] );
+								}
+								break;
+							}
+						}
+					}
+				}
+
+				// Apply converter (if not an equivalence)
+				if ( conv !== true ) {
+
+					// Unless errors are allowed to bubble, catch and return them
+					if ( conv && s[ "throws" ] ) {
+						response = conv( response );
+					} else {
+						try {
+							response = conv( response );
+						} catch ( e ) {
+							return { state: "parsererror", error: conv ? e : "No conversion from " + prev + " to " + current };
+						}
+					}
+				}
+			}
+		}
+	}
+
+	return { state: "success", data: response };
+}
+
+jQuery.extend({
+
+	// Counter for holding the number of active queries
+	active: 0,
+
+	// Last-Modified header cache for next request
+	lastModified: {},
+	etag: {},
+
+	ajaxSettings: {
+		url: ajaxLocation,
+		type: "GET",
+		isLocal: rlocalProtocol.test( ajaxLocParts[ 1 ] ),
+		global: true,
+		processData: true,
+		async: true,
+		contentType: "application/x-www-form-urlencoded; charset=UTF-8",
+		/*
+		timeout: 0,
+		data: null,
+		dataType: null,
+		username: null,
+		password: null,
+		cache: null,
+		throws: false,
+		traditional: false,
+		headers: {},
+		*/
+
+		accepts: {
+			"*": allTypes,
+			text: "text/plain",
+			html: "text/html",
+			xml: "application/xml, text/xml",
+			json: "application/json, text/javascript"
+		},
+
+		contents: {
+			xml: /xml/,
+			html: /html/,
+			json: /json/
+		},
+
+		responseFields: {
+			xml: "responseXML",
+			text: "responseText",
+			json: "responseJSON"
+		},
+
+		// Data converters
+		// Keys separate source (or catchall "*") and destination types with a single space
+		converters: {
+
+			// Convert anything to text
+			"* text": String,
+
+			// Text to html (true = no transformation)
+			"text html": true,
+
+			// Evaluate text as a json expression
+			"text json": jQuery.parseJSON,
+
+			// Parse text as xml
+			"text xml": jQuery.parseXML
+		},
+
+		// For options that shouldn't be deep extended:
+		// you can add your own custom options here if
+		// and when you create one that shouldn't be
+		// deep extended (see ajaxExtend)
+		flatOptions: {
+			url: true,
+			context: true
+		}
+	},
+
+	// Creates a full fledged settings object into target
+	// with both ajaxSettings and settings fields.
+	// If target is omitted, writes into ajaxSettings.
+	ajaxSetup: function( target, settings ) {
+		return settings ?
+
+			// Building a settings object
+			ajaxExtend( ajaxExtend( target, jQuery.ajaxSettings ), settings ) :
+
+			// Extending ajaxSettings
+			ajaxExtend( jQuery.ajaxSettings, target );
+	},
+
+	ajaxPrefilter: addToPrefiltersOrTransports( prefilters ),
+	ajaxTransport: addToPrefiltersOrTransports( transports ),
+
+	// Main method
+	ajax: function( url, options ) {
+
+		// If url is an object, simulate pre-1.5 signature
+		if ( typeof url === "object" ) {
+			options = url;
+			url = undefined;
+		}
+
+		// Force options to be an object
+		options = options || {};
+
+		var transport,
+			// URL without anti-cache param
+			cacheURL,
+			// Response headers
+			responseHeadersString,
+			responseHeaders,
+			// timeout handle
+			timeoutTimer,
+			// Cross-domain detection vars
+			parts,
+			// To know if global events are to be dispatched
+			fireGlobals,
+			// Loop variable
+			i,
+			// Create the final options object
+			s = jQuery.ajaxSetup( {}, options ),
+			// Callbacks context
+			callbackContext = s.context || s,
+			// Context for global events is callbackContext if it is a DOM node or jQuery collection
+			globalEventContext = s.context && ( callbackContext.nodeType || callbackContext.jquery ) ?
+				jQuery( callbackContext ) :
+				jQuery.event,
+			// Deferreds
+			deferred = jQuery.Deferred(),
+			completeDeferred = jQuery.Callbacks("once memory"),
+			// Status-dependent callbacks
+			statusCode = s.statusCode || {},
+			// Headers (they are sent all at once)
+			requestHeaders = {},
+			requestHeadersNames = {},
+			// The jqXHR state
+			state = 0,
+			// Default abort message
+			strAbort = "canceled",
+			// Fake xhr
+			jqXHR = {
+				readyState: 0,
+
+				// Builds headers hashtable if needed
+				getResponseHeader: function( key ) {
+					var match;
+					if ( state === 2 ) {
+						if ( !responseHeaders ) {
+							responseHeaders = {};
+							while ( (match = rheaders.exec( responseHeadersString )) ) {
+								responseHeaders[ match[1].toLowerCase() ] = match[ 2 ];
+							}
+						}
+						match = responseHeaders[ key.toLowerCase() ];
+					}
+					return match == null ? null : match;
+				},
+
+				// Raw string
+				getAllResponseHeaders: function() {
+					return state === 2 ? responseHeadersString : null;
+				},
+
+				// Caches the header
+				setRequestHeader: function( name, value ) {
+					var lname = name.toLowerCase();
+					if ( !state ) {
+						name = requestHeadersNames[ lname ] = requestHeadersNames[ lname ] || name;
+						requestHeaders[ name ] = value;
+					}
+					return this;
+				},
+
+				// Overrides response content-type header
+				overrideMimeType: function( type ) {
+					if ( !state ) {
+						s.mimeType = type;
+					}
+					return this;
+				},
+
+				// Status-dependent callbacks
+				statusCode: function( map ) {
+					var code;
+					if ( map ) {
+						if ( state < 2 ) {
+							for ( code in map ) {
+								// Lazy-add the new callback in a way that preserves old ones
+								statusCode[ code ] = [ statusCode[ code ], map[ code ] ];
+							}
+						} else {
+							// Execute the appropriate callbacks
+							jqXHR.always( map[ jqXHR.status ] );
+						}
+					}
+					return this;
+				},
+
+				// Cancel the request
+				abort: function( statusText ) {
+					var finalText = statusText || strAbort;
+					if ( transport ) {
+						transport.abort( finalText );
+					}
+					done( 0, finalText );
+					return this;
+				}
+			};
+
+		// Attach deferreds
+		deferred.promise( jqXHR ).complete = completeDeferred.add;
+		jqXHR.success = jqXHR.done;
+		jqXHR.error = jqXHR.fail;
+
+		// Remove hash character (#7531: and string promotion)
+		// Add protocol if not provided (prefilters might expect it)
+		// Handle falsy url in the settings object (#10093: consistency with old signature)
+		// We also use the url parameter if available
+		s.url = ( ( url || s.url || ajaxLocation ) + "" ).replace( rhash, "" )
+			.replace( rprotocol, ajaxLocParts[ 1 ] + "//" );
+
+		// Alias method option to type as per ticket #12004
+		s.type = options.method || options.type || s.method || s.type;
+
+		// Extract dataTypes list
+		s.dataTypes = jQuery.trim( s.dataType || "*" ).toLowerCase().match( rnotwhite ) || [ "" ];
+
+		// A cross-domain request is in order when we have a protocol:host:port mismatch
+		if ( s.crossDomain == null ) {
+			parts = rurl.exec( s.url.toLowerCase() );
+			s.crossDomain = !!( parts &&
+				( parts[ 1 ] !== ajaxLocParts[ 1 ] || parts[ 2 ] !== ajaxLocParts[ 2 ] ||
+					( parts[ 3 ] || ( parts[ 1 ] === "http:" ? "80" : "443" ) ) !==
+						( ajaxLocParts[ 3 ] || ( ajaxLocParts[ 1 ] === "http:" ? "80" : "443" ) ) )
+			);
+		}
+
+		// Convert data if not already a string
+		if ( s.data && s.processData && typeof s.data !== "string" ) {
+			s.data = jQuery.param( s.data, s.traditional );
+		}
+
+		// Apply prefilters
+		inspectPrefiltersOrTransports( prefilters, s, options, jqXHR );
+
+		// If request was aborted inside a prefilter, stop there
+		if ( state === 2 ) {
+			return jqXHR;
+		}
+
+		// We can fire global events as of now if asked to
+		fireGlobals = s.global;
+
+		// Watch for a new set of requests
+		if ( fireGlobals && jQuery.active++ === 0 ) {
+			jQuery.event.trigger("ajaxStart");
+		}
+
+		// Uppercase the type
+		s.type = s.type.toUpperCase();
+
+		// Determine if request has content
+		s.hasContent = !rnoContent.test( s.type );
+
+		// Save the URL in case we're toying with the If-Modified-Since
+		// and/or If-None-Match header later on
+		cacheURL = s.url;
+
+		// More options handling for requests with no content
+		if ( !s.hasContent ) {
+
+			// If data is available, append data to url
+			if ( s.data ) {
+				cacheURL = ( s.url += ( rquery.test( cacheURL ) ? "&" : "?" ) + s.data );
+				// #9682: remove data so that it's not used in an eventual retry
+				delete s.data;
+			}
+
+			// Add anti-cache in url if needed
+			if ( s.cache === false ) {
+				s.url = rts.test( cacheURL ) ?
+
+					// If there is already a '_' parameter, set its value
+					cacheURL.replace( rts, "$1_=" + nonce++ ) :
+
+					// Otherwise add one to the end
+					cacheURL + ( rquery.test( cacheURL ) ? "&" : "?" ) + "_=" + nonce++;
+			}
+		}
+
+		// Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
+		if ( s.ifModified ) {
+			if ( jQuery.lastModified[ cacheURL ] ) {
+				jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified[ cacheURL ] );
+			}
+			if ( jQuery.etag[ cacheURL ] ) {
+				jqXHR.setRequestHeader( "If-None-Match", jQuery.etag[ cacheURL ] );
+			}
+		}
+
+		// Set the correct header, if data is being sent
+		if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) {
+			jqXHR.setRequestHeader( "Content-Type", s.contentType );
+		}
+
+		// Set the Accepts header for the server, depending on the dataType
+		jqXHR.setRequestHeader(
+			"Accept",
+			s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[0] ] ?
+				s.accepts[ s.dataTypes[0] ] + ( s.dataTypes[ 0 ] !== "*" ? ", " + allTypes + "; q=0.01" : "" ) :
+				s.accepts[ "*" ]
+		);
+
+		// Check for headers option
+		for ( i in s.headers ) {
+			jqXHR.setRequestHeader( i, s.headers[ i ] );
+		}
+
+		// Allow custom headers/mimetypes and early abort
+		if ( s.beforeSend && ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || state === 2 ) ) {
+			// Abort if not done already and return
+			return jqXHR.abort();
+		}
+
+		// aborting is no longer a cancellation
+		strAbort = "abort";
+
+		// Install callbacks on deferreds
+		for ( i in { success: 1, error: 1, complete: 1 } ) {
+			jqXHR[ i ]( s[ i ] );
+		}
+
+		// Get transport
+		transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR );
+
+		// If no transport, we auto-abort
+		if ( !transport ) {
+			done( -1, "No Transport" );
+		} else {
+			jqXHR.readyState = 1;
+
+			// Send global event
+			if ( fireGlobals ) {
+				globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] );
+			}
+			// Timeout
+			if ( s.async && s.timeout > 0 ) {
+				timeoutTimer = setTimeout(function() {
+					jqXHR.abort("timeout");
+				}, s.timeout );
+			}
+
+			try {
+				state = 1;
+				transport.send( requestHeaders, done );
+			} catch ( e ) {
+				// Propagate exception as error if not done
+				if ( state < 2 ) {
+					done( -1, e );
+				// Simply rethrow otherwise
+				} else {
+					throw e;
+				}
+			}
+		}
+
+		// Callback for when everything is done
+		function done( status, nativeStatusText, responses, headers ) {
+			var isSuccess, success, error, response, modified,
+				statusText = nativeStatusText;
+
+			// Called once
+			if ( state === 2 ) {
+				return;
+			}
+
+			// State is "done" now
+			state = 2;
+
+			// Clear timeout if it exists
+			if ( timeoutTimer ) {
+				clearTimeout( timeoutTimer );
+			}
+
+			// Dereference transport for early garbage collection
+			// (no matter how long the jqXHR object will be used)
+			transport = undefined;
+
+			// Cache response headers
+			responseHeadersString = headers || "";
+
+			// Set readyState
+			jqXHR.readyState = status > 0 ? 4 : 0;
+
+			// Determine if successful
+			isSuccess = status >= 200 && status < 300 || status === 304;
+
+			// Get response data
+			if ( responses ) {
+				response = ajaxHandleResponses( s, jqXHR, responses );
+			}
+
+			// Convert no matter what (that way responseXXX fields are always set)
+			response = ajaxConvert( s, response, jqXHR, isSuccess );
+
+			// If successful, handle type chaining
+			if ( isSuccess ) {
+
+				// Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
+				if ( s.ifModified ) {
+					modified = jqXHR.getResponseHeader("Last-Modified");
+					if ( modified ) {
+						jQuery.lastModified[ cacheURL ] = modified;
+					}
+					modified = jqXHR.getResponseHeader("etag");
+					if ( modified ) {
+						jQuery.etag[ cacheURL ] = modified;
+					}
+				}
+
+				// if no content
+				if ( status === 204 || s.type === "HEAD" ) {
+					statusText = "nocontent";
+
+				// if not modified
+				} else if ( status === 304 ) {
+					statusText = "notmodified";
+
+				// If we have data, let's convert it
+				} else {
+					statusText = response.state;
+					success = response.data;
+					error = response.error;
+					isSuccess = !error;
+				}
+			} else {
+				// We extract error from statusText
+				// then normalize statusText and status for non-aborts
+				error = statusText;
+				if ( status || !statusText ) {
+					statusText = "error";
+					if ( status < 0 ) {
+						status = 0;
+					}
+				}
+			}
+
+			// Set data for the fake xhr object
+			jqXHR.status = status;
+			jqXHR.statusText = ( nativeStatusText || statusText ) + "";
+
+			// Success/Error
+			if ( isSuccess ) {
+				deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] );
+			} else {
+				deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] );
+			}
+
+			// Status-dependent callbacks
+			jqXHR.statusCode( statusCode );
+			statusCode = undefined;
+
+			if ( fireGlobals ) {
+				globalEventContext.trigger( isSuccess ? "ajaxSuccess" : "ajaxError",
+					[ jqXHR, s, isSuccess ? success : error ] );
+			}
+
+			// Complete
+			completeDeferred.fireWith( callbackContext, [ jqXHR, statusText ] );
+
+			if ( fireGlobals ) {
+				globalEventContext.trigger( "ajaxComplete", [ jqXHR, s ] );
+				// Handle the global AJAX counter
+				if ( !( --jQuery.active ) ) {
+					jQuery.event.trigger("ajaxStop");
+				}
+			}
+		}
+
+		return jqXHR;
+	},
+
+	getJSON: function( url, data, callback ) {
+		return jQuery.get( url, data, callback, "json" );
+	},
+
+	getScript: function( url, callback ) {
+		return jQuery.get( url, undefined, callback, "script" );
+	}
+});
+
+jQuery.each( [ "get", "post" ], function( i, method ) {
+	jQuery[ method ] = function( url, data, callback, type ) {
+		// shift arguments if data argument was omitted
+		if ( jQuery.isFunction( data ) ) {
+			type = type || callback;
+			callback = data;
+			data = undefined;
+		}
+
+		return jQuery.ajax({
+			url: url,
+			type: method,
+			dataType: type,
+			data: data,
+			success: callback
+		});
+	};
+});
+
+// Attach a bunch of functions for handling common AJAX events
+jQuery.each( [ "ajaxStart", "ajaxStop", "ajaxComplete", "ajaxError", "ajaxSuccess", "ajaxSend" ], function( i, type ) {
+	jQuery.fn[ type ] = function( fn ) {
+		return this.on( type, fn );
+	};
+});
+
+
+jQuery._evalUrl = function( url ) {
+	return jQuery.ajax({
+		url: url,
+		type: "GET",
+		dataType: "script",
+		async: false,
+		global: false,
+		"throws": true
+	});
+};
+
+
+jQuery.fn.extend({
+	wrapAll: function( html ) {
+		var wrap;
+
+		if ( jQuery.isFunction( html ) ) {
+			return this.each(function( i ) {
+				jQuery( this ).wrapAll( html.call(this, i) );
+			});
+		}
+
+		if ( this[ 0 ] ) {
+
+			// The elements to wrap the target around
+			wrap = jQuery( html, this[ 0 ].ownerDocument ).eq( 0 ).clone( true );
+
+			if ( this[ 0 ].parentNode ) {
+				wrap.insertBefore( this[ 0 ] );
+			}
+
+			wrap.map(function() {
+				var elem = this;
+
+				while ( elem.firstElementChild ) {
+					elem = elem.firstElementChild;
+				}
+
+				return elem;
+			}).append( this );
+		}
+
+		return this;
+	},
+
+	wrapInner: function( html ) {
+		if ( jQuery.isFunction( html ) ) {
+			return this.each(function( i ) {
+				jQuery( this ).wrapInner( html.call(this, i) );
+			});
+		}
+
+		return this.each(function() {
+			var self = jQuery( this ),
+				contents = self.contents();
+
+			if ( contents.length ) {
+				contents.wrapAll( html );
+
+			} else {
+				self.append( html );
+			}
+		});
+	},
+
+	wrap: function( html ) {
+		var isFunction = jQuery.isFunction( html );
+
+		return this.each(function( i ) {
+			jQuery( this ).wrapAll( isFunction ? html.call(this, i) : html );
+		});
+	},
+
+	unwrap: function() {
+		return this.parent().each(function() {
+			if ( !jQuery.nodeName( this, "body" ) ) {
+				jQuery( this ).replaceWith( this.childNodes );
+			}
+		}).end();
+	}
+});
+
+
+jQuery.expr.filters.hidden = function( elem ) {
+	// Support: Opera <= 12.12
+	// Opera reports offsetWidths and offsetHeights less than zero on some elements
+	return elem.offsetWidth <= 0 && elem.offsetHeight <= 0;
+};
+jQuery.expr.filters.visible = function( elem ) {
+	return !jQuery.expr.filters.hidden( elem );
+};
+
+
+
+
+var r20 = /%20/g,
+	rbracket = /\[\]$/,
+	rCRLF = /\r?\n/g,
+	rsubmitterTypes = /^(?:submit|button|image|reset|file)$/i,
+	rsubmittable = /^(?:input|select|textarea|keygen)/i;
+
+function buildParams( prefix, obj, traditional, add ) {
+	var name;
+
+	if ( jQuery.isArray( obj ) ) {
+		// Serialize array item.
+		jQuery.each( obj, function( i, v ) {
+			if ( traditional || rbracket.test( prefix ) ) {
+				// Treat each array item as a scalar.
+				add( prefix, v );
+
+			} else {
+				// Item is non-scalar (array or object), encode its numeric index.
+				buildParams( prefix + "[" + ( typeof v === "object" ? i : "" ) + "]", v, traditional, add );
+			}
+		});
+
+	} else if ( !traditional && jQuery.type( obj ) === "object" ) {
+		// Serialize object item.
+		for ( name in obj ) {
+			buildParams( prefix + "[" + name + "]", obj[ name ], traditional, add );
+		}
+
+	} else {
+		// Serialize scalar item.
+		add( prefix, obj );
+	}
+}
+
+// Serialize an array of form elements or a set of
+// key/values into a query string
+jQuery.param = function( a, traditional ) {
+	var prefix,
+		s = [],
+		add = function( key, value ) {
+			// If value is a function, invoke it and return its value
+			value = jQuery.isFunction( value ) ? value() : ( value == null ? "" : value );
+			s[ s.length ] = encodeURIComponent( key ) + "=" + encodeURIComponent( value );
+		};
+
+	// Set traditional to true for jQuery <= 1.3.2 behavior.
+	if ( traditional === undefined ) {
+		traditional = jQuery.ajaxSettings && jQuery.ajaxSettings.traditional;
+	}
+
+	// If an array was passed in, assume that it is an array of form elements.
+	if ( jQuery.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) {
+		// Serialize the form elements
+		jQuery.each( a, function() {
+			add( this.name, this.value );
+		});
+
+	} else {
+		// If traditional, encode the "old" way (the way 1.3.2 or older
+		// did it), otherwise encode params recursively.
+		for ( prefix in a ) {
+			buildParams( prefix, a[ prefix ], traditional, add );
+		}
+	}
+
+	// Return the resulting serialization
+	return s.join( "&" ).replace( r20, "+" );
+};
+
+jQuery.fn.extend({
+	serialize: function() {
+		return jQuery.param( this.serializeArray() );
+	},
+	serializeArray: function() {
+		return this.map(function() {
+			// Can add propHook for "elements" to filter or add form elements
+			var elements = jQuery.prop( this, "elements" );
+			return elements ? jQuery.makeArray( elements ) : this;
+		})
+		.filter(function() {
+			var type = this.type;
+
+			// Use .is( ":disabled" ) so that fieldset[disabled] works
+			return this.name && !jQuery( this ).is( ":disabled" ) &&
+				rsubmittable.test( this.nodeName ) && !rsubmitterTypes.test( type ) &&
+				( this.checked || !rcheckableType.test( type ) );
+		})
+		.map(function( i, elem ) {
+			var val = jQuery( this ).val();
+
+			return val == null ?
+				null :
+				jQuery.isArray( val ) ?
+					jQuery.map( val, function( val ) {
+						return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
+					}) :
+					{ name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
+		}).get();
+	}
+});
+
+
+jQuery.ajaxSettings.xhr = function() {
+	try {
+		return new XMLHttpRequest();
+	} catch( e ) {}
+};
+
+var xhrId = 0,
+	xhrCallbacks = {},
+	xhrSuccessStatus = {
+		// file protocol always yields status code 0, assume 200
+		0: 200,
+		// Support: IE9
+		// #1450: sometimes IE returns 1223 when it should be 204
+		1223: 204
+	},
+	xhrSupported = jQuery.ajaxSettings.xhr();
+
+// Support: IE9
+// Open requests must be manually aborted on unload (#5280)
+if ( window.ActiveXObject ) {
+	jQuery( window ).on( "unload", function() {
+		for ( var key in xhrCallbacks ) {
+			xhrCallbacks[ key ]();
+		}
+	});
+}
+
+support.cors = !!xhrSupported && ( "withCredentials" in xhrSupported );
+support.ajax = xhrSupported = !!xhrSupported;
+
+jQuery.ajaxTransport(function( options ) {
+	var callback;
+
+	// Cross domain only allowed if supported through XMLHttpRequest
+	if ( support.cors || xhrSupported && !options.crossDomain ) {
+		return {
+			send: function( headers, complete ) {
+				var i,
+					xhr = options.xhr(),
+					id = ++xhrId;
+
+				xhr.open( options.type, options.url, options.async, options.username, options.password );
+
+				// Apply custom fields if provided
+				if ( options.xhrFields ) {
+					for ( i in options.xhrFields ) {
+						xhr[ i ] = options.xhrFields[ i ];
+					}
+				}
+
+				// Override mime type if needed
+				if ( options.mimeType && xhr.overrideMimeType ) {
+					xhr.overrideMimeType( options.mimeType );
+				}
+
+				// X-Requested-With header
+				// For cross-domain requests, seeing as conditions for a preflight are
+				// akin to a jigsaw puzzle, we simply never set it to be sure.
+				// (it can always be set on a per-request basis or even using ajaxSetup)
+				// For same-domain requests, won't change header if already provided.
+				if ( !options.crossDomain && !headers["X-Requested-With"] ) {
+					headers["X-Requested-With"] = "XMLHttpRequest";
+				}
+
+				// Set headers
+				for ( i in headers ) {
+					xhr.setRequestHeader( i, headers[ i ] );
+				}
+
+				// Callback
+				callback = function( type ) {
+					return function() {
+						if ( callback ) {
+							delete xhrCallbacks[ id ];
+							callback = xhr.onload = xhr.onerror = null;
+
+							if ( type === "abort" ) {
+								xhr.abort();
+							} else if ( type === "error" ) {
+								complete(
+									// file: protocol always yields status 0; see #8605, #14207
+									xhr.status,
+									xhr.statusText
+								);
+							} else {
+								complete(
+									xhrSuccessStatus[ xhr.status ] || xhr.status,
+									xhr.statusText,
+									// Support: IE9
+									// Accessing binary-data responseText throws an exception
+									// (#11426)
+									typeof xhr.responseText === "string" ? {
+										text: xhr.responseText
+									} : undefined,
+									xhr.getAllResponseHeaders()
+								);
+							}
+						}
+					};
+				};
+
+				// Listen to events
+				xhr.onload = callback();
+				xhr.onerror = callback("error");
+
+				// Create the abort callback
+				callback = xhrCallbacks[ id ] = callback("abort");
+
+				try {
+					// Do send the request (this may raise an exception)
+					xhr.send( options.hasContent && options.data || null );
+				} catch ( e ) {
+					// #14683: Only rethrow if this hasn't been notified as an error yet
+					if ( callback ) {
+						throw e;
+					}
+				}
+			},
+
+			abort: function() {
+				if ( callback ) {
+					callback();
+				}
+			}
+		};
+	}
+});
+
+
+
+
+// Install script dataType
+jQuery.ajaxSetup({
+	accepts: {
+		script: "text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"
+	},
+	contents: {
+		script: /(?:java|ecma)script/
+	},
+	converters: {
+		"text script": function( text ) {
+			jQuery.globalEval( text );
+			return text;
+		}
+	}
+});
+
+// Handle cache's special case and crossDomain
+jQuery.ajaxPrefilter( "script", function( s ) {
+	if ( s.cache === undefined ) {
+		s.cache = false;
+	}
+	if ( s.crossDomain ) {
+		s.type = "GET";
+	}
+});
+
+// Bind script tag hack transport
+jQuery.ajaxTransport( "script", function( s ) {
+	// This transport only deals with cross domain requests
+	if ( s.crossDomain ) {
+		var script, callback;
+		return {
+			send: function( _, complete ) {
+				script = jQuery("<script>").prop({
+					async: true,
+					charset: s.scriptCharset,
+					src: s.url
+				}).on(
+					"load error",
+					callback = function( evt ) {
+						script.remove();
+						callback = null;
+						if ( evt ) {
+							complete( evt.type === "error" ? 404 : 200, evt.type );
+						}
+					}
+				);
+				document.head.appendChild( script[ 0 ] );
+			},
+			abort: function() {
+				if ( callback ) {
+					callback();
+				}
+			}
+		};
+	}
+});
+
+
+
+
+var oldCallbacks = [],
+	rjsonp = /(=)\?(?=&|$)|\?\?/;
+
+// Default jsonp settings
+jQuery.ajaxSetup({
+	jsonp: "callback",
+	jsonpCallback: function() {
+		var callback = oldCallbacks.pop() || ( jQuery.expando + "_" + ( nonce++ ) );
+		this[ callback ] = true;
+		return callback;
+	}
+});
+
+// Detect, normalize options and install callbacks for jsonp requests
+jQuery.ajaxPrefilter( "json jsonp", function( s, originalSettings, jqXHR ) {
+
+	var callbackName, overwritten, responseContainer,
+		jsonProp = s.jsonp !== false && ( rjsonp.test( s.url ) ?
+			"url" :
+			typeof s.data === "string" && !( s.contentType || "" ).indexOf("application/x-www-form-urlencoded") && rjsonp.test( s.data ) && "data"
+		);
+
+	// Handle iff the expected data type is "jsonp" or we have a parameter to set
+	if ( jsonProp || s.dataTypes[ 0 ] === "jsonp" ) {
+
+		// Get callback name, remembering preexisting value associated with it
+		callbackName = s.jsonpCallback = jQuery.isFunction( s.jsonpCallback ) ?
+			s.jsonpCallback() :
+			s.jsonpCallback;
+
+		// Insert callback into url or form data
+		if ( jsonProp ) {
+			s[ jsonProp ] = s[ jsonProp ].replace( rjsonp, "$1" + callbackName );
+		} else if ( s.jsonp !== false ) {
+			s.url += ( rquery.test( s.url ) ? "&" : "?" ) + s.jsonp + "=" + callbackName;
+		}
+
+		// Use data converter to retrieve json after script execution
+		s.converters["script json"] = function() {
+			if ( !responseContainer ) {
+				jQuery.error( callbackName + " was not called" );
+			}
+			return responseContainer[ 0 ];
+		};
+
+		// force json dataType
+		s.dataTypes[ 0 ] = "json";
+
+		// Install callback
+		overwritten = window[ callbackName ];
+		window[ callbackName ] = function() {
+			responseContainer = arguments;
+		};
+
+		// Clean-up function (fires after converters)
+		jqXHR.always(function() {
+			// Restore preexisting value
+			window[ callbackName ] = overwritten;
+
+			// Save back as free
+			if ( s[ callbackName ] ) {
+				// make sure that re-using the options doesn't screw things around
+				s.jsonpCallback = originalSettings.jsonpCallback;
+
+				// save the callback name for future use
+				oldCallbacks.push( callbackName );
+			}
+
+			// Call if it was a function and we have a response
+			if ( responseContainer && jQuery.isFunction( overwritten ) ) {
+				overwritten( responseContainer[ 0 ] );
+			}
+
+			responseContainer = overwritten = undefined;
+		});
+
+		// Delegate to script
+		return "script";
+	}
+});
+
+
+
+
+// data: string of html
+// context (optional): If specified, the fragment will be created in this context, defaults to document
+// keepScripts (optional): If true, will include scripts passed in the html string
+jQuery.parseHTML = function( data, context, keepScripts ) {
+	if ( !data || typeof data !== "string" ) {
+		return null;
+	}
+	if ( typeof context === "boolean" ) {
+		keepScripts = context;
+		context = false;
+	}
+	context = context || document;
+
+	var parsed = rsingleTag.exec( data ),
+		scripts = !keepScripts && [];
+
+	// Single tag
+	if ( parsed ) {
+		return [ context.createElement( parsed[1] ) ];
+	}
+
+	parsed = jQuery.buildFragment( [ data ], context, scripts );
+
+	if ( scripts && scripts.length ) {
+		jQuery( scripts ).remove();
+	}
+
+	return jQuery.merge( [], parsed.childNodes );
+};
+
+
+// Keep a copy of the old load method
+var _load = jQuery.fn.load;
+
+/**
+ * Load a url into a page
+ */
+jQuery.fn.load = function( url, params, callback ) {
+	if ( typeof url !== "string" && _load ) {
+		return _load.apply( this, arguments );
+	}
+
+	var selector, type, response,
+		self = this,
+		off = url.indexOf(" ");
+
+	if ( off >= 0 ) {
+		selector = jQuery.trim( url.slice( off ) );
+		url = url.slice( 0, off );
+	}
+
+	// If it's a function
+	if ( jQuery.isFunction( params ) ) {
+
+		// We assume that it's the callback
+		callback = params;
+		params = undefined;
+
+	// Otherwise, build a param string
+	} else if ( params && typeof params === "object" ) {
+		type = "POST";
+	}
+
+	// If we have elements to modify, make the request
+	if ( self.length > 0 ) {
+		jQuery.ajax({
+			url: url,
+
+			// if "type" variable is undefined, then "GET" method will be used
+			type: type,
+			dataType: "html",
+			data: params
+		}).done(function( responseText ) {
+
+			// Save response for use in complete callback
+			response = arguments;
+
+			self.html( selector ?
+
+				// If a selector was specified, locate the right elements in a dummy div
+				// Exclude scripts to avoid IE 'Permission Denied' errors
+				jQuery("<div>").append( jQuery.parseHTML( responseText ) ).find( selector ) :
+
+				// Otherwise use the full result
+				responseText );
+
+		}).complete( callback && function( jqXHR, status ) {
+			self.each( callback, response || [ jqXHR.responseText, status, jqXHR ] );
+		});
+	}
+
+	return this;
+};
+
+
+
+
+jQuery.expr.filters.animated = function( elem ) {
+	return jQuery.grep(jQuery.timers, function( fn ) {
+		return elem === fn.elem;
+	}).length;
+};
+
+
+
+
+var docElem = window.document.documentElement;
+
+/**
+ * Gets a window from an element
+ */
+function getWindow( elem ) {
+	return jQuery.isWindow( elem ) ? elem : elem.nodeType === 9 && elem.defaultView;
+}
+
+jQuery.offset = {
+	setOffset: function( elem, options, i ) {
+		var curPosition, curLeft, curCSSTop, curTop, curOffset, curCSSLeft, calculatePosition,
+			position = jQuery.css( elem, "position" ),
+			curElem = jQuery( elem ),
+			props = {};
+
+		// Set position first, in-case top/left are set even on static elem
+		if ( position === "static" ) {
+			elem.style.position = "relative";
+		}
+
+		curOffset = curElem.offset();
+		curCSSTop = jQuery.css( elem, "top" );
+		curCSSLeft = jQuery.css( elem, "left" );
+		calculatePosition = ( position === "absolute" || position === "fixed" ) &&
+			( curCSSTop + curCSSLeft ).indexOf("auto") > -1;
+
+		// Need to be able to calculate position if either top or left is auto and position is either absolute or fixed
+		if ( calculatePosition ) {
+			curPosition = curElem.position();
+			curTop = curPosition.top;
+			curLeft = curPosition.left;
+
+		} else {
+			curTop = parseFloat( curCSSTop ) || 0;
+			curLeft = parseFloat( curCSSLeft ) || 0;
+		}
+
+		if ( jQuery.isFunction( options ) ) {
+			options = options.call( elem, i, curOffset );
+		}
+
+		if ( options.top != null ) {
+			props.top = ( options.top - curOffset.top ) + curTop;
+		}
+		if ( options.left != null ) {
+			props.left = ( options.left - curOffset.left ) + curLeft;
+		}
+
+		if ( "using" in options ) {
+			options.using.call( elem, props );
+
+		} else {
+			curElem.css( props );
+		}
+	}
+};
+
+jQuery.fn.extend({
+	offset: function( options ) {
+		if ( arguments.length ) {
+			return options === undefined ?
+				this :
+				this.each(function( i ) {
+					jQuery.offset.setOffset( this, options, i );
+				});
+		}
+
+		var docElem, win,
+			elem = this[ 0 ],
+			box = { top: 0, left: 0 },
+			doc = elem && elem.ownerDocument;
+
+		if ( !doc ) {
+			return;
+		}
+
+		docElem = doc.documentElement;
+
+		// Make sure it's not a disconnected DOM node
+		if ( !jQuery.contains( docElem, elem ) ) {
+			return box;
+		}
+
+		// If we don't have gBCR, just use 0,0 rather than error
+		// BlackBerry 5, iOS 3 (original iPhone)
+		if ( typeof elem.getBoundingClientRect !== strundefined ) {
+			box = elem.getBoundingClientRect();
+		}
+		win = getWindow( doc );
+		return {
+			top: box.top + win.pageYOffset - docElem.clientTop,
+			left: box.left + win.pageXOffset - docElem.clientLeft
+		};
+	},
+
+	position: function() {
+		if ( !this[ 0 ] ) {
+			return;
+		}
+
+		var offsetParent, offset,
+			elem = this[ 0 ],
+			parentOffset = { top: 0, left: 0 };
+
+		// Fixed elements are offset from window (parentOffset = {top:0, left: 0}, because it is its only offset parent
+		if ( jQuery.css( elem, "position" ) === "fixed" ) {
+			// We assume that getBoundingClientRect is available when computed position is fixed
+			offset = elem.getBoundingClientRect();
+
+		} else {
+			// Get *real* offsetParent
+			offsetParent = this.offsetParent();
+
+			// Get correct offsets
+			offset = this.offset();
+			if ( !jQuery.nodeName( offsetParent[ 0 ], "html" ) ) {
+				parentOffset = offsetParent.offset();
+			}
+
+			// Add offsetParent borders
+			parentOffset.top += jQuery.css( offsetParent[ 0 ], "borderTopWidth", true );
+			parentOffset.left += jQuery.css( offsetParent[ 0 ], "borderLeftWidth", true );
+		}
+
+		// Subtract parent offsets and element margins
+		return {
+			top: offset.top - parentOffset.top - jQuery.css( elem, "marginTop", true ),
+			left: offset.left - parentOffset.left - jQuery.css( elem, "marginLeft", true )
+		};
+	},
+
+	offsetParent: function() {
+		return this.map(function() {
+			var offsetParent = this.offsetParent || docElem;
+
+			while ( offsetParent && ( !jQuery.nodeName( offsetParent, "html" ) && jQuery.css( offsetParent, "position" ) === "static" ) ) {
+				offsetParent = offsetParent.offsetParent;
+			}
+
+			return offsetParent || docElem;
+		});
+	}
+});
+
+// Create scrollLeft and scrollTop methods
+jQuery.each( { scrollLeft: "pageXOffset", scrollTop: "pageYOffset" }, function( method, prop ) {
+	var top = "pageYOffset" === prop;
+
+	jQuery.fn[ method ] = function( val ) {
+		return access( this, function( elem, method, val ) {
+			var win = getWindow( elem );
+
+			if ( val === undefined ) {
+				return win ? win[ prop ] : elem[ method ];
+			}
+
+			if ( win ) {
+				win.scrollTo(
+					!top ? val : window.pageXOffset,
+					top ? val : window.pageYOffset
+				);
+
+			} else {
+				elem[ method ] = val;
+			}
+		}, method, val, arguments.length, null );
+	};
+});
+
+// Add the top/left cssHooks using jQuery.fn.position
+// Webkit bug: https://bugs.webkit.org/show_bug.cgi?id=29084
+// getComputedStyle returns percent when specified for top/left/bottom/right
+// rather than make the css module depend on the offset module, we just check for it here
+jQuery.each( [ "top", "left" ], function( i, prop ) {
+	jQuery.cssHooks[ prop ] = addGetHookIf( support.pixelPosition,
+		function( elem, computed ) {
+			if ( computed ) {
+				computed = curCSS( elem, prop );
+				// if curCSS returns percentage, fallback to offset
+				return rnumnonpx.test( computed ) ?
+					jQuery( elem ).position()[ prop ] + "px" :
+					computed;
+			}
+		}
+	);
+});
+
+
+// Create innerHeight, innerWidth, height, width, outerHeight and outerWidth methods
+jQuery.each( { Height: "height", Width: "width" }, function( name, type ) {
+	jQuery.each( { padding: "inner" + name, content: type, "": "outer" + name }, function( defaultExtra, funcName ) {
+		// margin is only for outerHeight, outerWidth
+		jQuery.fn[ funcName ] = function( margin, value ) {
+			var chainable = arguments.length && ( defaultExtra || typeof margin !== "boolean" ),
+				extra = defaultExtra || ( margin === true || value === true ? "margin" : "border" );
+
+			return access( this, function( elem, type, value ) {
+				var doc;
+
+				if ( jQuery.isWindow( elem ) ) {
+					// As of 5/8/2012 this will yield incorrect results for Mobile Safari, but there
+					// isn't a whole lot we can do. See pull request at this URL for discussion:
+					// https://github.com/jquery/jquery/pull/764
+					return elem.document.documentElement[ "client" + name ];
+				}
+
+				// Get document width or height
+				if ( elem.nodeType === 9 ) {
+					doc = elem.documentElement;
+
+					// Either scroll[Width/Height] or offset[Width/Height] or client[Width/Height],
+					// whichever is greatest
+					return Math.max(
+						elem.body[ "scroll" + name ], doc[ "scroll" + name ],
+						elem.body[ "offset" + name ], doc[ "offset" + name ],
+						doc[ "client" + name ]
+					);
+				}
+
+				return value === undefined ?
+					// Get width or height on the element, requesting but not forcing parseFloat
+					jQuery.css( elem, type, extra ) :
+
+					// Set width or height on the element
+					jQuery.style( elem, type, value, extra );
+			}, type, chainable ? margin : undefined, chainable, null );
+		};
+	});
+});
+
+
+// The number of elements contained in the matched element set
+jQuery.fn.size = function() {
+	return this.length;
+};
+
+jQuery.fn.andSelf = jQuery.fn.addBack;
+
+
+
+
+// Register as a named AMD module, since jQuery can be concatenated with other
+// files that may use define, but not via a proper concatenation script that
+// understands anonymous AMD modules. A named AMD is safest and most robust
+// way to register. Lowercase jquery is used because AMD module names are
+// derived from file names, and jQuery is normally delivered in a lowercase
+// file name. Do this after creating the global so that if an AMD module wants
+// to call noConflict to hide this version of jQuery, it will work.
+
+// Note that for maximum portability, libraries that are not jQuery should
+// declare themselves as anonymous modules, and avoid setting a global if an
+// AMD loader is present. jQuery is a special case. For more information, see
+// https://github.com/jrburke/requirejs/wiki/Updating-existing-libraries#wiki-anon
+
+if ( typeof define === "function" && define.amd ) {
+	define( "jquery", [], function() {
+		return jQuery;
+	});
+}
+
+
+
+
+var
+	// Map over jQuery in case of overwrite
+	_jQuery = window.jQuery,
+
+	// Map over the $ in case of overwrite
+	_$ = window.$;
+
+jQuery.noConflict = function( deep ) {
+	if ( window.$ === jQuery ) {
+		window.$ = _$;
+	}
+
+	if ( deep && window.jQuery === jQuery ) {
+		window.jQuery = _jQuery;
+	}
+
+	return jQuery;
+};
+
+// Expose jQuery and $ identifiers, even in
+// AMD (#7102#comment:10, https://github.com/jquery/jquery/pull/557)
+// and CommonJS for browser emulators (#13566)
+if ( typeof noGlobal === strundefined ) {
+	window.jQuery = window.$ = jQuery;
+}
+
+
+
+
+return jQuery;
+
+}));
diff --git a/packages/context-coloring/benchmark/fixtures/lodash-2.4.1.js b/packages/context-coloring/benchmark/fixtures/lodash-2.4.1.js
new file mode 100644
index 0000000..d653e5a
--- /dev/null
+++ b/packages/context-coloring/benchmark/fixtures/lodash-2.4.1.js
@@ -0,0 +1,6785 @@
+/**
+ * @license
+ * Lo-Dash 2.4.1 (Custom Build) <http://lodash.com/>
+ * Build: `lodash modern -o ./dist/lodash.js`
+ * Copyright 2012-2013 The Dojo Foundation <http://dojofoundation.org/>
+ * Based on Underscore.js 1.5.2 <http://underscorejs.org/LICENSE>
+ * Copyright 2009-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
+ * Available under MIT license <http://lodash.com/license>
+ */
+;(function() {
+
+  /** Used as a safe reference for `undefined` in pre ES5 environments */
+  var undefined;
+
+  /** Used to pool arrays and objects used internally */
+  var arrayPool = [],
+      objectPool = [];
+
+  /** Used to generate unique IDs */
+  var idCounter = 0;
+
+  /** Used to prefix keys to avoid issues with `__proto__` and properties on `Object.prototype` */
+  var keyPrefix = +new Date + '';
+
+  /** Used as the size when optimizations are enabled for large arrays */
+  var largeArraySize = 75;
+
+  /** Used as the max size of the `arrayPool` and `objectPool` */
+  var maxPoolSize = 40;
+
+  /** Used to detect and test whitespace */
+  var whitespace = (
+    // whitespace
+    ' \t\x0B\f\xA0\ufeff' +
+
+    // line terminators
+    '\n\r\u2028\u2029' +
+
+    // unicode category "Zs" space separators
+    '\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000'
+  );
+
+  /** Used to match empty string literals in compiled template source */
+  var reEmptyStringLeading = /\b__p \+= '';/g,
+      reEmptyStringMiddle = /\b(__p \+=) '' \+/g,
+      reEmptyStringTrailing = /(__e\(.*?\)|\b__t\)) \+\n'';/g;
+
+  /**
+   * Used to match ES6 template delimiters
+   * http://people.mozilla.org/~jorendorff/es6-draft.html#sec-literals-string-literals
+   */
+  var reEsTemplate = /\$\{([^\\}]*(?:\\.[^\\}]*)*)\}/g;
+
+  /** Used to match regexp flags from their coerced string values */
+  var reFlags = /\w*$/;
+
+  /** Used to detected named functions */
+  var reFuncName = /^\s*function[ \n\r\t]+\w/;
+
+  /** Used to match "interpolate" template delimiters */
+  var reInterpolate = /<%=([\s\S]+?)%>/g;
+
+  /** Used to match leading whitespace and zeros to be removed */
+  var reLeadingSpacesAndZeros = RegExp('^[' + whitespace + ']*0+(?=.$)');
+
+  /** Used to ensure capturing order of template delimiters */
+  var reNoMatch = /($^)/;
+
+  /** Used to detect functions containing a `this` reference */
+  var reThis = /\bthis\b/;
+
+  /** Used to match unescaped characters in compiled string literals */
+  var reUnescapedString = /['\n\r\t\u2028\u2029\\]/g;
+
+  /** Used to assign default `context` object properties */
+  var contextProps = [
+    'Array', 'Boolean', 'Date', 'Function', 'Math', 'Number', 'Object',
+    'RegExp', 'String', '_', 'attachEvent', 'clearTimeout', 'isFinite', 'isNaN',
+    'parseInt', 'setTimeout'
+  ];
+
+  /** Used to make template sourceURLs easier to identify */
+  var templateCounter = 0;
+
+  /** `Object#toString` result shortcuts */
+  var argsClass = '[object Arguments]',
+      arrayClass = '[object Array]',
+      boolClass = '[object Boolean]',
+      dateClass = '[object Date]',
+      funcClass = '[object Function]',
+      numberClass = '[object Number]',
+      objectClass = '[object Object]',
+      regexpClass = '[object RegExp]',
+      stringClass = '[object String]';
+
+  /** Used to identify object classifications that `_.clone` supports */
+  var cloneableClasses = {};
+  cloneableClasses[funcClass] = false;
+  cloneableClasses[argsClass] = cloneableClasses[arrayClass] =
+  cloneableClasses[boolClass] = cloneableClasses[dateClass] =
+  cloneableClasses[numberClass] = cloneableClasses[objectClass] =
+  cloneableClasses[regexpClass] = cloneableClasses[stringClass] = true;
+
+  /** Used as an internal `_.debounce` options object */
+  var debounceOptions = {
+    'leading': false,
+    'maxWait': 0,
+    'trailing': false
+  };
+
+  /** Used as the property descriptor for `__bindData__` */
+  var descriptor = {
+    'configurable': false,
+    'enumerable': false,
+    'value': null,
+    'writable': false
+  };
+
+  /** Used to determine if values are of the language type Object */
+  var objectTypes = {
+    'boolean': false,
+    'function': true,
+    'object': true,
+    'number': false,
+    'string': false,
+    'undefined': false
+  };
+
+  /** Used to escape characters for inclusion in compiled string literals */
+  var stringEscapes = {
+    '\\': '\\',
+    "'": "'",
+    '\n': 'n',
+    '\r': 'r',
+    '\t': 't',
+    '\u2028': 'u2028',
+    '\u2029': 'u2029'
+  };
+
+  /** Used as a reference to the global object */
+  var root = (objectTypes[typeof window] && window) || this;
+
+  /** Detect free variable `exports` */
+  var freeExports = objectTypes[typeof exports] && exports && !exports.nodeType && exports;
+
+  /** Detect free variable `module` */
+  var freeModule = objectTypes[typeof module] && module && !module.nodeType && module;
+
+  /** Detect the popular CommonJS extension `module.exports` */
+  var moduleExports = freeModule && freeModule.exports === freeExports && freeExports;
+
+  /** Detect free variable `global` from Node.js or Browserified code and use it as `root` */
+  var freeGlobal = objectTypes[typeof global] && global;
+  if (freeGlobal && (freeGlobal.global === freeGlobal || freeGlobal.window === freeGlobal)) {
+    root = freeGlobal;
+  }
+
+  /*--------------------------------------------------------------------------*/
+
+  /**
+   * The base implementation of `_.indexOf` without support for binary searches
+   * or `fromIndex` constraints.
+   *
+   * @private
+   * @param {Array} array The array to search.
+   * @param {*} value The value to search for.
+   * @param {number} [fromIndex=0] The index to search from.
+   * @returns {number} Returns the index of the matched value or `-1`.
+   */
+  function baseIndexOf(array, value, fromIndex) {
+    var index = (fromIndex || 0) - 1,
+        length = array ? array.length : 0;
+
+    while (++index < length) {
+      if (array[index] === value) {
+        return index;
+      }
+    }
+    return -1;
+  }
+
+  /**
+   * An implementation of `_.contains` for cache objects that mimics the return
+   * signature of `_.indexOf` by returning `0` if the value is found, else `-1`.
+   *
+   * @private
+   * @param {Object} cache The cache object to inspect.
+   * @param {*} value The value to search for.
+   * @returns {number} Returns `0` if `value` is found, else `-1`.
+   */
+  function cacheIndexOf(cache, value) {
+    var type = typeof value;
+    cache = cache.cache;
+
+    if (type == 'boolean' || value == null) {
+      return cache[value] ? 0 : -1;
+    }
+    if (type != 'number' && type != 'string') {
+      type = 'object';
+    }
+    var key = type == 'number' ? value : keyPrefix + value;
+    cache = (cache = cache[type]) && cache[key];
+
+    return type == 'object'
+      ? (cache && baseIndexOf(cache, value) > -1 ? 0 : -1)
+      : (cache ? 0 : -1);
+  }
+
+  /**
+   * Adds a given value to the corresponding cache object.
+   *
+   * @private
+   * @param {*} value The value to add to the cache.
+   */
+  function cachePush(value) {
+    var cache = this.cache,
+        type = typeof value;
+
+    if (type == 'boolean' || value == null) {
+      cache[value] = true;
+    } else {
+      if (type != 'number' && type != 'string') {
+        type = 'object';
+      }
+      var key = type == 'number' ? value : keyPrefix + value,
+          typeCache = cache[type] || (cache[type] = {});
+
+      if (type == 'object') {
+        (typeCache[key] || (typeCache[key] = [])).push(value);
+      } else {
+        typeCache[key] = true;
+      }
+    }
+  }
+
+  /**
+   * Used by `_.max` and `_.min` as the default callback when a given
+   * collection is a string value.
+   *
+   * @private
+   * @param {string} value The character to inspect.
+   * @returns {number} Returns the code unit of given character.
+   */
+  function charAtCallback(value) {
+    return value.charCodeAt(0);
+  }
+
+  /**
+   * Used by `sortBy` to compare transformed `collection` elements, stable sorting
+   * them in ascending order.
+   *
+   * @private
+   * @param {Object} a The object to compare to `b`.
+   * @param {Object} b The object to compare to `a`.
+   * @returns {number} Returns the sort order indicator of `1` or `-1`.
+   */
+  function compareAscending(a, b) {
+    var ac = a.criteria,
+        bc = b.criteria,
+        index = -1,
+        length = ac.length;
+
+    while (++index < length) {
+      var value = ac[index],
+          other = bc[index];
+
+      if (value !== other) {
+        if (value > other || typeof value == 'undefined') {
+          return 1;
+        }
+        if (value < other || typeof other == 'undefined') {
+          return -1;
+        }
+      }
+    }
+    // Fixes an `Array#sort` bug in the JS engine embedded in Adobe applications
+    // that causes it, under certain circumstances, to return the same value for
+    // `a` and `b`. See https://github.com/jashkenas/underscore/pull/1247
+    //
+    // This also ensures a stable sort in V8 and other engines.
+    // See http://code.google.com/p/v8/issues/detail?id=90
+    return a.index - b.index;
+  }
+
+  /**
+   * Creates a cache object to optimize linear searches of large arrays.
+   *
+   * @private
+   * @param {Array} [array=[]] The array to search.
+   * @returns {null|Object} Returns the cache object or `null` if caching should not be used.
+   */
+  function createCache(array) {
+    var index = -1,
+        length = array.length,
+        first = array[0],
+        mid = array[(length / 2) | 0],
+        last = array[length - 1];
+
+    if (first && typeof first == 'object' &&
+        mid && typeof mid == 'object' && last && typeof last == 'object') {
+      return false;
+    }
+    var cache = getObject();
+    cache['false'] = cache['null'] = cache['true'] = cache['undefined'] = false;
+
+    var result = getObject();
+    result.array = array;
+    result.cache = cache;
+    result.push = cachePush;
+
+    while (++index < length) {
+      result.push(array[index]);
+    }
+    return result;
+  }
+
+  /**
+   * Used by `template` to escape characters for inclusion in compiled
+   * string literals.
+   *
+   * @private
+   * @param {string} match The matched character to escape.
+   * @returns {string} Returns the escaped character.
+   */
+  function escapeStringChar(match) {
+    return '\\' + stringEscapes[match];
+  }
+
+  /**
+   * Gets an array from the array pool or creates a new one if the pool is empty.
+   *
+   * @private
+   * @returns {Array} The array from the pool.
+   */
+  function getArray() {
+    return arrayPool.pop() || [];
+  }
+
+  /**
+   * Gets an object from the object pool or creates a new one if the pool is empty.
+   *
+   * @private
+   * @returns {Object} The object from the pool.
+   */
+  function getObject() {
+    return objectPool.pop() || {
+      'array': null,
+      'cache': null,
+      'criteria': null,
+      'false': false,
+      'index': 0,
+      'null': false,
+      'number': null,
+      'object': null,
+      'push': null,
+      'string': null,
+      'true': false,
+      'undefined': false,
+      'value': null
+    };
+  }
+
+  /**
+   * Releases the given array back to the array pool.
+   *
+   * @private
+   * @param {Array} [array] The array to release.
+   */
+  function releaseArray(array) {
+    array.length = 0;
+    if (arrayPool.length < maxPoolSize) {
+      arrayPool.push(array);
+    }
+  }
+
+  /**
+   * Releases the given object back to the object pool.
+   *
+   * @private
+   * @param {Object} [object] The object to release.
+   */
+  function releaseObject(object) {
+    var cache = object.cache;
+    if (cache) {
+      releaseObject(cache);
+    }
+    object.array = object.cache = object.criteria = object.object = object.number = object.string = object.value = null;
+    if (objectPool.length < maxPoolSize) {
+      objectPool.push(object);
+    }
+  }
+
+  /**
+   * Slices the `collection` from the `start` index up to, but not including,
+   * the `end` index.
+   *
+   * Note: This function is used instead of `Array#slice` to support node lists
+   * in IE < 9 and to ensure dense arrays are returned.
+   *
+   * @private
+   * @param {Array|Object|string} collection The collection to slice.
+   * @param {number} start The start index.
+   * @param {number} end The end index.
+   * @returns {Array} Returns the new array.
+   */
+  function slice(array, start, end) {
+    start || (start = 0);
+    if (typeof end == 'undefined') {
+      end = array ? array.length : 0;
+    }
+    var index = -1,
+        length = end - start || 0,
+        result = Array(length < 0 ? 0 : length);
+
+    while (++index < length) {
+      result[index] = array[start + index];
+    }
+    return result;
+  }
+
+  /*--------------------------------------------------------------------------*/
+
+  /**
+   * Create a new `lodash` function using the given context object.
+   *
+   * @static
+   * @memberOf _
+   * @category Utilities
+   * @param {Object} [context=root] The context object.
+   * @returns {Function} Returns the `lodash` function.
+   */
+  function runInContext(context) {
+    // Avoid issues with some ES3 environments that attempt to use values, named
+    // after built-in constructors like `Object`, for the creation of literals.
+    // ES5 clears this up by stating that literals must use built-in constructors.
+    // See http://es5.github.io/#x11.1.5.
+    context = context ? _.defaults(root.Object(), context, _.pick(root, contextProps)) : root;
+
+    /** Native constructor references */
+    var Array = context.Array,
+        Boolean = context.Boolean,
+        Date = context.Date,
+        Function = context.Function,
+        Math = context.Math,
+        Number = context.Number,
+        Object = context.Object,
+        RegExp = context.RegExp,
+        String = context.String,
+        TypeError = context.TypeError;
+
+    /**
+     * Used for `Array` method references.
+     *
+     * Normally `Array.prototype` would suffice, however, using an array literal
+     * avoids issues in Narwhal.
+     */
+    var arrayRef = [];
+
+    /** Used for native method references */
+    var objectProto = Object.prototype;
+
+    /** Used to restore the original `_` reference in `noConflict` */
+    var oldDash = context._;
+
+    /** Used to resolve the internal [[Class]] of values */
+    var toString = objectProto.toString;
+
+    /** Used to detect if a method is native */
+    var reNative = RegExp('^' +
+      String(toString)
+        .replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
+        .replace(/toString| for [^\]]+/g, '.*?') + '$'
+    );
+
+    /** Native method shortcuts */
+    var ceil = Math.ceil,
+        clearTimeout = context.clearTimeout,
+        floor = Math.floor,
+        fnToString = Function.prototype.toString,
+        getPrototypeOf = isNative(getPrototypeOf = Object.getPrototypeOf) && getPrototypeOf,
+        hasOwnProperty = objectProto.hasOwnProperty,
+        push = arrayRef.push,
+        setTimeout = context.setTimeout,
+        splice = arrayRef.splice,
+        unshift = arrayRef.unshift;
+
+    /** Used to set meta data on functions */
+    var defineProperty = (function() {
+      // IE 8 only accepts DOM elements
+      try {
+        var o = {},
+            func = isNative(func = Object.defineProperty) && func,
+            result = func(o, o, o) && func;
+      } catch(e) { }
+      return result;
+    }());
+
+    /* Native method shortcuts for methods with the same name as other `lodash` methods */
+    var nativeCreate = isNative(nativeCreate = Object.create) && nativeCreate,
+        nativeIsArray = isNative(nativeIsArray = Array.isArray) && nativeIsArray,
+        nativeIsFinite = context.isFinite,
+        nativeIsNaN = context.isNaN,
+        nativeKeys = isNative(nativeKeys = Object.keys) && nativeKeys,
+        nativeMax = Math.max,
+        nativeMin = Math.min,
+        nativeParseInt = context.parseInt,
+        nativeRandom = Math.random;
+
+    /** Used to lookup a built-in constructor by [[Class]] */
+    var ctorByClass = {};
+    ctorByClass[arrayClass] = Array;
+    ctorByClass[boolClass] = Boolean;
+    ctorByClass[dateClass] = Date;
+    ctorByClass[funcClass] = Function;
+    ctorByClass[objectClass] = Object;
+    ctorByClass[numberClass] = Number;
+    ctorByClass[regexpClass] = RegExp;
+    ctorByClass[stringClass] = String;
+
+    /*--------------------------------------------------------------------------*/
+
+    /**
+     * Creates a `lodash` object which wraps the given value to enable intuitive
+     * method chaining.
+     *
+     * In addition to Lo-Dash methods, wrappers also have the following `Array` methods:
+     * `concat`, `join`, `pop`, `push`, `reverse`, `shift`, `slice`, `sort`, `splice`,
+     * and `unshift`
+     *
+     * Chaining is supported in custom builds as long as the `value` method is
+     * implicitly or explicitly included in the build.
+     *
+     * The chainable wrapper functions are:
+     * `after`, `assign`, `bind`, `bindAll`, `bindKey`, `chain`, `compact`,
+     * `compose`, `concat`, `countBy`, `create`, `createCallback`, `curry`,
+     * `debounce`, `defaults`, `defer`, `delay`, `difference`, `filter`, `flatten`,
+     * `forEach`, `forEachRight`, `forIn`, `forInRight`, `forOwn`, `forOwnRight`,
+     * `functions`, `groupBy`, `indexBy`, `initial`, `intersection`, `invert`,
+     * `invoke`, `keys`, `map`, `max`, `memoize`, `merge`, `min`, `object`, `omit`,
+     * `once`, `pairs`, `partial`, `partialRight`, `pick`, `pluck`, `pull`, `push`,
+     * `range`, `reject`, `remove`, `rest`, `reverse`, `shuffle`, `slice`, `sort`,
+     * `sortBy`, `splice`, `tap`, `throttle`, `times`, `toArray`, `transform`,
+     * `union`, `uniq`, `unshift`, `unzip`, `values`, `where`, `without`, `wrap`,
+     * and `zip`
+     *
+     * The non-chainable wrapper functions are:
+     * `clone`, `cloneDeep`, `contains`, `escape`, `every`, `find`, `findIndex`,
+     * `findKey`, `findLast`, `findLastIndex`, `findLastKey`, `has`, `identity`,
+     * `indexOf`, `isArguments`, `isArray`, `isBoolean`, `isDate`, `isElement`,
+     * `isEmpty`, `isEqual`, `isFinite`, `isFunction`, `isNaN`, `isNull`, `isNumber`,
+     * `isObject`, `isPlainObject`, `isRegExp`, `isString`, `isUndefined`, `join`,
+     * `lastIndexOf`, `mixin`, `noConflict`, `parseInt`, `pop`, `random`, `reduce`,
+     * `reduceRight`, `result`, `shift`, `size`, `some`, `sortedIndex`, `runInContext`,
+     * `template`, `unescape`, `uniqueId`, and `value`
+     *
+     * The wrapper functions `first` and `last` return wrapped values when `n` is
+     * provided, otherwise they return unwrapped values.
+     *
+     * Explicit chaining can be enabled by using the `_.chain` method.
+     *
+     * @name _
+     * @constructor
+     * @category Chaining
+     * @param {*} value The value to wrap in a `lodash` instance.
+     * @returns {Object} Returns a `lodash` instance.
+     * @example
+     *
+     * var wrapped = _([1, 2, 3]);
+     *
+     * // returns an unwrapped value
+     * wrapped.reduce(function(sum, num) {
+     *   return sum + num;
+     * });
+     * // => 6
+     *
+     * // returns a wrapped value
+     * var squares = wrapped.map(function(num) {
+     *   return num * num;
+     * });
+     *
+     * _.isArray(squares);
+     * // => false
+     *
+     * _.isArray(squares.value());
+     * // => true
+     */
+    function lodash(value) {
+      // don't wrap if already wrapped, even if wrapped by a different `lodash` constructor
+      return (value && typeof value == 'object' && !isArray(value) && hasOwnProperty.call(value, '__wrapped__'))
+       ? value
+       : new lodashWrapper(value);
+    }
+
+    /**
+     * A fast path for creating `lodash` wrapper objects.
+     *
+     * @private
+     * @param {*} value The value to wrap in a `lodash` instance.
+     * @param {boolean} chainAll A flag to enable chaining for all methods
+     * @returns {Object} Returns a `lodash` instance.
+     */
+    function lodashWrapper(value, chainAll) {
+      this.__chain__ = !!chainAll;
+      this.__wrapped__ = value;
+    }
+    // ensure `new lodashWrapper` is an instance of `lodash`
+    lodashWrapper.prototype = lodash.prototype;
+
+    /**
+     * An object used to flag environments features.
+     *
+     * @static
+     * @memberOf _
+     * @type Object
+     */
+    var support = lodash.support = {};
+
+    /**
+     * Detect if functions can be decompiled by `Function#toString`
+     * (all but PS3 and older Opera mobile browsers & avoided in Windows 8 apps).
+     *
+     * @memberOf _.support
+     * @type boolean
+     */
+    support.funcDecomp = !isNative(context.WinRTError) && reThis.test(runInContext);
+
+    /**
+     * Detect if `Function#name` is supported (all but IE).
+     *
+     * @memberOf _.support
+     * @type boolean
+     */
+    support.funcNames = typeof Function.name == 'string';
+
+    /**
+     * By default, the template delimiters used by Lo-Dash are similar to those in
+     * embedded Ruby (ERB). Change the following template settings to use alternative
+     * delimiters.
+     *
+     * @static
+     * @memberOf _
+     * @type Object
+     */
+    lodash.templateSettings = {
+
+      /**
+       * Used to detect `data` property values to be HTML-escaped.
+       *
+       * @memberOf _.templateSettings
+       * @type RegExp
+       */
+      'escape': /<%-([\s\S]+?)%>/g,
+
+      /**
+       * Used to detect code to be evaluated.
+       *
+       * @memberOf _.templateSettings
+       * @type RegExp
+       */
+      'evaluate': /<%([\s\S]+?)%>/g,
+
+      /**
+       * Used to detect `data` property values to inject.
+       *
+       * @memberOf _.templateSettings
+       * @type RegExp
+       */
+      'interpolate': reInterpolate,
+
+      /**
+       * Used to reference the data object in the template text.
+       *
+       * @memberOf _.templateSettings
+       * @type string
+       */
+      'variable': '',
+
+      /**
+       * Used to import variables into the compiled template.
+       *
+       * @memberOf _.templateSettings
+       * @type Object
+       */
+      'imports': {
+
+        /**
+         * A reference to the `lodash` function.
+         *
+         * @memberOf _.templateSettings.imports
+         * @type Function
+         */
+        '_': lodash
+      }
+    };
+
+    /*--------------------------------------------------------------------------*/
+
+    /**
+     * The base implementation of `_.bind` that creates the bound function and
+     * sets its meta data.
+     *
+     * @private
+     * @param {Array} bindData The bind data array.
+     * @returns {Function} Returns the new bound function.
+     */
+    function baseBind(bindData) {
+      var func = bindData[0],
+          partialArgs = bindData[2],
+          thisArg = bindData[4];
+
+      function bound() {
+        // `Function#bind` spec
+        // http://es5.github.io/#x15.3.4.5
+        if (partialArgs) {
+          // avoid `arguments` object deoptimizations by using `slice` instead
+          // of `Array.prototype.slice.call` and not assigning `arguments` to a
+          // variable as a ternary expression
+          var args = slice(partialArgs);
+          push.apply(args, arguments);
+        }
+        // mimic the constructor's `return` behavior
+        // http://es5.github.io/#x13.2.2
+        if (this instanceof bound) {
+          // ensure `new bound` is an instance of `func`
+          var thisBinding = baseCreate(func.prototype),
+              result = func.apply(thisBinding, args || arguments);
+          return isObject(result) ? result : thisBinding;
+        }
+        return func.apply(thisArg, args || arguments);
+      }
+      setBindData(bound, bindData);
+      return bound;
+    }
+
+    /**
+     * The base implementation of `_.clone` without argument juggling or support
+     * for `thisArg` binding.
+     *
+     * @private
+     * @param {*} value The value to clone.
+     * @param {boolean} [isDeep=false] Specify a deep clone.
+     * @param {Function} [callback] The function to customize cloning values.
+     * @param {Array} [stackA=[]] Tracks traversed source objects.
+     * @param {Array} [stackB=[]] Associates clones with source counterparts.
+     * @returns {*} Returns the cloned value.
+     */
+    function baseClone(value, isDeep, callback, stackA, stackB) {
+      if (callback) {
+        var result = callback(value);
+        if (typeof result != 'undefined') {
+          return result;
+        }
+      }
+      // inspect [[Class]]
+      var isObj = isObject(value);
+      if (isObj) {
+        var className = toString.call(value);
+        if (!cloneableClasses[className]) {
+          return value;
+        }
+        var ctor = ctorByClass[className];
+        switch (className) {
+          case boolClass:
+          case dateClass:
+            return new ctor(+value);
+
+          case numberClass:
+          case stringClass:
+            return new ctor(value);
+
+          case regexpClass:
+            result = ctor(value.source, reFlags.exec(value));
+            result.lastIndex = value.lastIndex;
+            return result;
+        }
+      } else {
+        return value;
+      }
+      var isArr = isArray(value);
+      if (isDeep) {
+        // check for circular references and return corresponding clone
+        var initedStack = !stackA;
+        stackA || (stackA = getArray());
+        stackB || (stackB = getArray());
+
+        var length = stackA.length;
+        while (length--) {
+          if (stackA[length] == value) {
+            return stackB[length];
+          }
+        }
+        result = isArr ? ctor(value.length) : {};
+      }
+      else {
+        result = isArr ? slice(value) : assign({}, value);
+      }
+      // add array properties assigned by `RegExp#exec`
+      if (isArr) {
+        if (hasOwnProperty.call(value, 'index')) {
+          result.index = value.index;
+        }
+        if (hasOwnProperty.call(value, 'input')) {
+          result.input = value.input;
+        }
+      }
+      // exit for shallow clone
+      if (!isDeep) {
+        return result;
+      }
+      // add the source value to the stack of traversed objects
+      // and associate it with its clone
+      stackA.push(value);
+      stackB.push(result);
+
+      // recursively populate clone (susceptible to call stack limits)
+      (isArr ? forEach : forOwn)(value, function(objValue, key) {
+        result[key] = baseClone(objValue, isDeep, callback, stackA, stackB);
+      });
+
+      if (initedStack) {
+        releaseArray(stackA);
+        releaseArray(stackB);
+      }
+      return result;
+    }
+
+    /**
+     * The base implementation of `_.create` without support for assigning
+     * properties to the created object.
+     *
+     * @private
+     * @param {Object} prototype The object to inherit from.
+     * @returns {Object} Returns the new object.
+     */
+    function baseCreate(prototype, properties) {
+      return isObject(prototype) ? nativeCreate(prototype) : {};
+    }
+    // fallback for browsers without `Object.create`
+    if (!nativeCreate) {
+      baseCreate = (function() {
+        function Object() {}
+        return function(prototype) {
+          if (isObject(prototype)) {
+            Object.prototype = prototype;
+            var result = new Object;
+            Object.prototype = null;
+          }
+          return result || context.Object();
+        };
+      }());
+    }
+
+    /**
+     * The base implementation of `_.createCallback` without support for creating
+     * "_.pluck" or "_.where" style callbacks.
+     *
+     * @private
+     * @param {*} [func=identity] The value to convert to a callback.
+     * @param {*} [thisArg] The `this` binding of the created callback.
+     * @param {number} [argCount] The number of arguments the callback accepts.
+     * @returns {Function} Returns a callback function.
+     */
+    function baseCreateCallback(func, thisArg, argCount) {
+      if (typeof func != 'function') {
+        return identity;
+      }
+      // exit early for no `thisArg` or already bound by `Function#bind`
+      if (typeof thisArg == 'undefined' || !('prototype' in func)) {
+        return func;
+      }
+      var bindData = func.__bindData__;
+      if (typeof bindData == 'undefined') {
+        if (support.funcNames) {
+          bindData = !func.name;
+        }
+        bindData = bindData || !support.funcDecomp;
+        if (!bindData) {
+          var source = fnToString.call(func);
+          if (!support.funcNames) {
+            bindData = !reFuncName.test(source);
+          }
+          if (!bindData) {
+            // checks if `func` references the `this` keyword and stores the result
+            bindData = reThis.test(source);
+            setBindData(func, bindData);
+          }
+        }
+      }
+      // exit early if there are no `this` references or `func` is bound
+      if (bindData === false || (bindData !== true && bindData[1] & 1)) {
+        return func;
+      }
+      switch (argCount) {
+        case 1: return function(value) {
+          return func.call(thisArg, value);
+        };
+        case 2: return function(a, b) {
+          return func.call(thisArg, a, b);
+        };
+        case 3: return function(value, index, collection) {
+          return func.call(thisArg, value, index, collection);
+        };
+        case 4: return function(accumulator, value, index, collection) {
+          return func.call(thisArg, accumulator, value, index, collection);
+        };
+      }
+      return bind(func, thisArg);
+    }
+
+    /**
+     * The base implementation of `createWrapper` that creates the wrapper and
+     * sets its meta data.
+     *
+     * @private
+     * @param {Array} bindData The bind data array.
+     * @returns {Function} Returns the new function.
+     */
+    function baseCreateWrapper(bindData) {
+      var func = bindData[0],
+          bitmask = bindData[1],
+          partialArgs = bindData[2],
+          partialRightArgs = bindData[3],
+          thisArg = bindData[4],
+          arity = bindData[5];
+
+      var isBind = bitmask & 1,
+          isBindKey = bitmask & 2,
+          isCurry = bitmask & 4,
+          isCurryBound = bitmask & 8,
+          key = func;
+
+      function bound() {
+        var thisBinding = isBind ? thisArg : this;
+        if (partialArgs) {
+          var args = slice(partialArgs);
+          push.apply(args, arguments);
+        }
+        if (partialRightArgs || isCurry) {
+          args || (args = slice(arguments));
+          if (partialRightArgs) {
+            push.apply(args, partialRightArgs);
+          }
+          if (isCurry && args.length < arity) {
+            bitmask |= 16 & ~32;
+            return baseCreateWrapper([func, (isCurryBound ? bitmask : bitmask & ~3), args, null, thisArg, arity]);
+          }
+        }
+        args || (args = arguments);
+        if (isBindKey) {
+          func = thisBinding[key];
+        }
+        if (this instanceof bound) {
+          thisBinding = baseCreate(func.prototype);
+          var result = func.apply(thisBinding, args);
+          return isObject(result) ? result : thisBinding;
+        }
+        return func.apply(thisBinding, args);
+      }
+      setBindData(bound, bindData);
+      return bound;
+    }
+
+    /**
+     * The base implementation of `_.difference` that accepts a single array
+     * of values to exclude.
+     *
+     * @private
+     * @param {Array} array The array to process.
+     * @param {Array} [values] The array of values to exclude.
+     * @returns {Array} Returns a new array of filtered values.
+     */
+    function baseDifference(array, values) {
+      var index = -1,
+          indexOf = getIndexOf(),
+          length = array ? array.length : 0,
+          isLarge = length >= largeArraySize && indexOf === baseIndexOf,
+          result = [];
+
+      if (isLarge) {
+        var cache = createCache(values);
+        if (cache) {
+          indexOf = cacheIndexOf;
+          values = cache;
+        } else {
+          isLarge = false;
+        }
+      }
+      while (++index < length) {
+        var value = array[index];
+        if (indexOf(values, value) < 0) {
+          result.push(value);
+        }
+      }
+      if (isLarge) {
+        releaseObject(values);
+      }
+      return result;
+    }
+
+    /**
+     * The base implementation of `_.flatten` without support for callback
+     * shorthands or `thisArg` binding.
+     *
+     * @private
+     * @param {Array} array The array to flatten.
+     * @param {boolean} [isShallow=false] A flag to restrict flattening to a single level.
+     * @param {boolean} [isStrict=false] A flag to restrict flattening to arrays and `arguments` objects.
+     * @param {number} [fromIndex=0] The index to start from.
+     * @returns {Array} Returns a new flattened array.
+     */
+    function baseFlatten(array, isShallow, isStrict, fromIndex) {
+      var index = (fromIndex || 0) - 1,
+          length = array ? array.length : 0,
+          result = [];
+
+      while (++index < length) {
+        var value = array[index];
+
+        if (value && typeof value == 'object' && typeof value.length == 'number'
+            && (isArray(value) || isArguments(value))) {
+          // recursively flatten arrays (susceptible to call stack limits)
+          if (!isShallow) {
+            value = baseFlatten(value, isShallow, isStrict);
+          }
+          var valIndex = -1,
+              valLength = value.length,
+              resIndex = result.length;
+
+          result.length += valLength;
+          while (++valIndex < valLength) {
+            result[resIndex++] = value[valIndex];
+          }
+        } else if (!isStrict) {
+          result.push(value);
+        }
+      }
+      return result;
+    }
+
+    /**
+     * The base implementation of `_.isEqual`, without support for `thisArg` binding,
+     * that allows partial "_.where" style comparisons.
+     *
+     * @private
+     * @param {*} a The value to compare.
+     * @param {*} b The other value to compare.
+     * @param {Function} [callback] The function to customize comparing values.
+     * @param {Function} [isWhere=false] A flag to indicate performing partial comparisons.
+     * @param {Array} [stackA=[]] Tracks traversed `a` objects.
+     * @param {Array} [stackB=[]] Tracks traversed `b` objects.
+     * @returns {boolean} Returns `true` if the values are equivalent, else `false`.
+     */
+    function baseIsEqual(a, b, callback, isWhere, stackA, stackB) {
+      // used to indicate that when comparing objects, `a` has at least the properties of `b`
+      if (callback) {
+        var result = callback(a, b);
+        if (typeof result != 'undefined') {
+          return !!result;
+        }
+      }
+      // exit early for identical values
+      if (a === b) {
+        // treat `+0` vs. `-0` as not equal
+        return a !== 0 || (1 / a == 1 / b);
+      }
+      var type = typeof a,
+          otherType = typeof b;
+
+      // exit early for unlike primitive values
+      if (a === a &&
+          !(a && objectTypes[type]) &&
+          !(b && objectTypes[otherType])) {
+        return false;
+      }
+      // exit early for `null` and `undefined` avoiding ES3's Function#call behavior
+      // http://es5.github.io/#x15.3.4.4
+      if (a == null || b == null) {
+        return a === b;
+      }
+      // compare [[Class]] names
+      var className = toString.call(a),
+          otherClass = toString.call(b);
+
+      if (className == argsClass) {
+        className = objectClass;
+      }
+      if (otherClass == argsClass) {
+        otherClass = objectClass;
+      }
+      if (className != otherClass) {
+        return false;
+      }
+      switch (className) {
+        case boolClass:
+        case dateClass:
+          // coerce dates and booleans to numbers, dates to milliseconds and booleans
+          // to `1` or `0` treating invalid dates coerced to `NaN` as not equal
+          return +a == +b;
+
+        case numberClass:
+          // treat `NaN` vs. `NaN` as equal
+          return (a != +a)
+            ? b != +b
+            // but treat `+0` vs. `-0` as not equal
+            : (a == 0 ? (1 / a == 1 / b) : a == +b);
+
+        case regexpClass:
+        case stringClass:
+          // coerce regexes to strings (http://es5.github.io/#x15.10.6.4)
+          // treat string primitives and their corresponding object instances as equal
+          return a == String(b);
+      }
+      var isArr = className == arrayClass;
+      if (!isArr) {
+        // unwrap any `lodash` wrapped values
+        var aWrapped = hasOwnProperty.call(a, '__wrapped__'),
+            bWrapped = hasOwnProperty.call(b, '__wrapped__');
+
+        if (aWrapped || bWrapped) {
+          return baseIsEqual(aWrapped ? a.__wrapped__ : a, bWrapped ? b.__wrapped__ : b, callback, isWhere, stackA, stackB);
+        }
+        // exit for functions and DOM nodes
+        if (className != objectClass) {
+          return false;
+        }
+        // in older versions of Opera, `arguments` objects have `Array` constructors
+        var ctorA = a.constructor,
+            ctorB = b.constructor;
+
+        // non `Object` object instances with different constructors are not equal
+        if (ctorA != ctorB &&
+              !(isFunction(ctorA) && ctorA instanceof ctorA && isFunction(ctorB) && ctorB instanceof ctorB) &&
+              ('constructor' in a && 'constructor' in b)
+            ) {
+          return false;
+        }
+      }
+      // assume cyclic structures are equal
+      // the algorithm for detecting cyclic structures is adapted from ES 5.1
+      // section 15.12.3, abstract operation `JO` (http://es5.github.io/#x15.12.3)
+      var initedStack = !stackA;
+      stackA || (stackA = getArray());
+      stackB || (stackB = getArray());
+
+      var length = stackA.length;
+      while (length--) {
+        if (stackA[length] == a) {
+          return stackB[length] == b;
+        }
+      }
+      var size = 0;
+      result = true;
+
+      // add `a` and `b` to the stack of traversed objects
+      stackA.push(a);
+      stackB.push(b);
+
+      // recursively compare objects and arrays (susceptible to call stack limits)
+      if (isArr) {
+        // compare lengths to determine if a deep comparison is necessary
+        length = a.length;
+        size = b.length;
+        result = size == length;
+
+        if (result || isWhere) {
+          // deep compare the contents, ignoring non-numeric properties
+          while (size--) {
+            var index = length,
+                value = b[size];
+
+            if (isWhere) {
+              while (index--) {
+                if ((result = baseIsEqual(a[index], value, callback, isWhere, stackA, stackB))) {
+                  break;
+                }
+              }
+            } else if (!(result = baseIsEqual(a[size], value, callback, isWhere, stackA, stackB))) {
+              break;
+            }
+          }
+        }
+      }
+      else {
+        // deep compare objects using `forIn`, instead of `forOwn`, to avoid `Object.keys`
+        // which, in this case, is more costly
+        forIn(b, function(value, key, b) {
+          if (hasOwnProperty.call(b, key)) {
+            // count the number of properties.
+            size++;
+            // deep compare each property value.
+            return (result = hasOwnProperty.call(a, key) && baseIsEqual(a[key], value, callback, isWhere, stackA, stackB));
+          }
+        });
+
+        if (result && !isWhere) {
+          // ensure both objects have the same number of properties
+          forIn(a, function(value, key, a) {
+            if (hasOwnProperty.call(a, key)) {
+              // `size` will be `-1` if `a` has more properties than `b`
+              return (result = --size > -1);
+            }
+          });
+        }
+      }
+      stackA.pop();
+      stackB.pop();
+
+      if (initedStack) {
+        releaseArray(stackA);
+        releaseArray(stackB);
+      }
+      return result;
+    }
+
+    /**
+     * The base implementation of `_.merge` without argument juggling or support
+     * for `thisArg` binding.
+     *
+     * @private
+     * @param {Object} object The destination object.
+     * @param {Object} source The source object.
+     * @param {Function} [callback] The function to customize merging properties.
+     * @param {Array} [stackA=[]] Tracks traversed source objects.
+     * @param {Array} [stackB=[]] Associates values with source counterparts.
+     */
+    function baseMerge(object, source, callback, stackA, stackB) {
+      (isArray(source) ? forEach : forOwn)(source, function(source, key) {
+        var found,
+            isArr,
+            result = source,
+            value = object[key];
+
+        if (source && ((isArr = isArray(source)) || isPlainObject(source))) {
+          // avoid merging previously merged cyclic sources
+          var stackLength = stackA.length;
+          while (stackLength--) {
+            if ((found = stackA[stackLength] == source)) {
+              value = stackB[stackLength];
+              break;
+            }
+          }
+          if (!found) {
+            var isShallow;
+            if (callback) {
+              result = callback(value, source);
+              if ((isShallow = typeof result != 'undefined')) {
+                value = result;
+              }
+            }
+            if (!isShallow) {
+              value = isArr
+                ? (isArray(value) ? value : [])
+                : (isPlainObject(value) ? value : {});
+            }
+            // add `source` and associated `value` to the stack of traversed objects
+            stackA.push(source);
+            stackB.push(value);
+
+            // recursively merge objects and arrays (susceptible to call stack limits)
+            if (!isShallow) {
+              baseMerge(value, source, callback, stackA, stackB);
+            }
+          }
+        }
+        else {
+          if (callback) {
+            result = callback(value, source);
+            if (typeof result == 'undefined') {
+              result = source;
+            }
+          }
+          if (typeof result != 'undefined') {
+            value = result;
+          }
+        }
+        object[key] = value;
+      });
+    }
+
+    /**
+     * The base implementation of `_.random` without argument juggling or support
+     * for returning floating-point numbers.
+     *
+     * @private
+     * @param {number} min The minimum possible value.
+     * @param {number} max The maximum possible value.
+     * @returns {number} Returns a random number.
+     */
+    function baseRandom(min, max) {
+      return min + floor(nativeRandom() * (max - min + 1));
+    }
+
+    /**
+     * The base implementation of `_.uniq` without support for callback shorthands
+     * or `thisArg` binding.
+     *
+     * @private
+     * @param {Array} array The array to process.
+     * @param {boolean} [isSorted=false] A flag to indicate that `array` is sorted.
+     * @param {Function} [callback] The function called per iteration.
+     * @returns {Array} Returns a duplicate-value-free array.
+     */
+    function baseUniq(array, isSorted, callback) {
+      var index = -1,
+          indexOf = getIndexOf(),
+          length = array ? array.length : 0,
+          result = [];
+
+      var isLarge = !isSorted && length >= largeArraySize && indexOf === baseIndexOf,
+          seen = (callback || isLarge) ? getArray() : result;
+
+      if (isLarge) {
+        var cache = createCache(seen);
+        indexOf = cacheIndexOf;
+        seen = cache;
+      }
+      while (++index < length) {
+        var value = array[index],
+            computed = callback ? callback(value, index, array) : value;
+
+        if (isSorted
+              ? !index || seen[seen.length - 1] !== computed
+              : indexOf(seen, computed) < 0
+            ) {
+          if (callback || isLarge) {
+            seen.push(computed);
+          }
+          result.push(value);
+        }
+      }
+      if (isLarge) {
+        releaseArray(seen.array);
+        releaseObject(seen);
+      } else if (callback) {
+        releaseArray(seen);
+      }
+      return result;
+    }
+
+    /**
+     * Creates a function that aggregates a collection, creating an object composed
+     * of keys generated from the results of running each element of the collection
+     * through a callback. The given `setter` function sets the keys and values
+     * of the composed object.
+     *
+     * @private
+     * @param {Function} setter The setter function.
+     * @returns {Function} Returns the new aggregator function.
+     */
+    function createAggregator(setter) {
+      return function(collection, callback, thisArg) {
+        var result = {};
+        callback = lodash.createCallback(callback, thisArg, 3);
+
+        var index = -1,
+            length = collection ? collection.length : 0;
+
+        if (typeof length == 'number') {
+          while (++index < length) {
+            var value = collection[index];
+            setter(result, value, callback(value, index, collection), collection);
+          }
+        } else {
+          forOwn(collection, function(value, key, collection) {
+            setter(result, value, callback(value, key, collection), collection);
+          });
+        }
+        return result;
+      };
+    }
+
+    /**
+     * Creates a function that, when called, either curries or invokes `func`
+     * with an optional `this` binding and partially applied arguments.
+     *
+     * @private
+     * @param {Function|string} func The function or method name to reference.
+     * @param {number} bitmask The bitmask of method flags to compose.
+     *  The bitmask may be composed of the following flags:
+     *  1 - `_.bind`
+     *  2 - `_.bindKey`
+     *  4 - `_.curry`
+     *  8 - `_.curry` (bound)
+     *  16 - `_.partial`
+     *  32 - `_.partialRight`
+     * @param {Array} [partialArgs] An array of arguments to prepend to those
+     *  provided to the new function.
+     * @param {Array} [partialRightArgs] An array of arguments to append to those
+     *  provided to the new function.
+     * @param {*} [thisArg] The `this` binding of `func`.
+     * @param {number} [arity] The arity of `func`.
+     * @returns {Function} Returns the new function.
+     */
+    function createWrapper(func, bitmask, partialArgs, partialRightArgs, thisArg, arity) {
+      var isBind = bitmask & 1,
+          isBindKey = bitmask & 2,
+          isCurry = bitmask & 4,
+          isCurryBound = bitmask & 8,
+          isPartial = bitmask & 16,
+          isPartialRight = bitmask & 32;
+
+      if (!isBindKey && !isFunction(func)) {
+        throw new TypeError;
+      }
+      if (isPartial && !partialArgs.length) {
+        bitmask &= ~16;
+        isPartial = partialArgs = false;
+      }
+      if (isPartialRight && !partialRightArgs.length) {
+        bitmask &= ~32;
+        isPartialRight = partialRightArgs = false;
+      }
+      var bindData = func && func.__bindData__;
+      if (bindData && bindData !== true) {
+        // clone `bindData`
+        bindData = slice(bindData);
+        if (bindData[2]) {
+          bindData[2] = slice(bindData[2]);
+        }
+        if (bindData[3]) {
+          bindData[3] = slice(bindData[3]);
+        }
+        // set `thisBinding` is not previously bound
+        if (isBind && !(bindData[1] & 1)) {
+          bindData[4] = thisArg;
+        }
+        // set if previously bound but not currently (subsequent curried functions)
+        if (!isBind && bindData[1] & 1) {
+          bitmask |= 8;
+        }
+        // set curried arity if not yet set
+        if (isCurry && !(bindData[1] & 4)) {
+          bindData[5] = arity;
+        }
+        // append partial left arguments
+        if (isPartial) {
+          push.apply(bindData[2] || (bindData[2] = []), partialArgs);
+        }
+        // append partial right arguments
+        if (isPartialRight) {
+          unshift.apply(bindData[3] || (bindData[3] = []), partialRightArgs);
+        }
+        // merge flags
+        bindData[1] |= bitmask;
+        return createWrapper.apply(null, bindData);
+      }
+      // fast path for `_.bind`
+      var creater = (bitmask == 1 || bitmask === 17) ? baseBind : baseCreateWrapper;
+      return creater([func, bitmask, partialArgs, partialRightArgs, thisArg, arity]);
+    }
+
+    /**
+     * Used by `escape` to convert characters to HTML entities.
+     *
+     * @private
+     * @param {string} match The matched character to escape.
+     * @returns {string} Returns the escaped character.
+     */
+    function escapeHtmlChar(match) {
+      return htmlEscapes[match];
+    }
+
+    /**
+     * Gets the appropriate "indexOf" function. If the `_.indexOf` method is
+     * customized, this method returns the custom method, otherwise it returns
+     * the `baseIndexOf` function.
+     *
+     * @private
+     * @returns {Function} Returns the "indexOf" function.
+     */
+    function getIndexOf() {
+      var result = (result = lodash.indexOf) === indexOf ? baseIndexOf : result;
+      return result;
+    }
+
+    /**
+     * Checks if `value` is a native function.
+     *
+     * @private
+     * @param {*} value The value to check.
+     * @returns {boolean} Returns `true` if the `value` is a native function, else `false`.
+     */
+    function isNative(value) {
+      return typeof value == 'function' && reNative.test(value);
+    }
+
+    /**
+     * Sets `this` binding data on a given function.
+     *
+     * @private
+     * @param {Function} func The function to set data on.
+     * @param {Array} value The data array to set.
+     */
+    var setBindData = !defineProperty ? noop : function(func, value) {
+      descriptor.value = value;
+      defineProperty(func, '__bindData__', descriptor);
+    };
+
+    /**
+     * A fallback implementation of `isPlainObject` which checks if a given value
+     * is an object created by the `Object` constructor, assuming objects created
+     * by the `Object` constructor have no inherited enumerable properties and that
+     * there are no `Object.prototype` extensions.
+     *
+     * @private
+     * @param {*} value The value to check.
+     * @returns {boolean} Returns `true` if `value` is a plain object, else `false`.
+     */
+    function shimIsPlainObject(value) {
+      var ctor,
+          result;
+
+      // avoid non Object objects, `arguments` objects, and DOM elements
+      if (!(value && toString.call(value) == objectClass) ||
+          (ctor = value.constructor, isFunction(ctor) && !(ctor instanceof ctor))) {
+        return false;
+      }
+      // In most environments an object's own properties are iterated before
+      // its inherited properties. If the last iterated property is an object's
+      // own property then there are no inherited enumerable properties.
+      forIn(value, function(value, key) {
+        result = key;
+      });
+      return typeof result == 'undefined' || hasOwnProperty.call(value, result);
+    }
+
+    /**
+     * Used by `unescape` to convert HTML entities to characters.
+     *
+     * @private
+     * @param {string} match The matched character to unescape.
+     * @returns {string} Returns the unescaped character.
+     */
+    function unescapeHtmlChar(match) {
+      return htmlUnescapes[match];
+    }
+
+    /*--------------------------------------------------------------------------*/
+
+    /**
+     * Checks if `value` is an `arguments` object.
+     *
+     * @static
+     * @memberOf _
+     * @category Objects
+     * @param {*} value The value to check.
+     * @returns {boolean} Returns `true` if the `value` is an `arguments` object, else `false`.
+     * @example
+     *
+     * (function() { return _.isArguments(arguments); })(1, 2, 3);
+     * // => true
+     *
+     * _.isArguments([1, 2, 3]);
+     * // => false
+     */
+    function isArguments(value) {
+      return value && typeof value == 'object' && typeof value.length == 'number' &&
+        toString.call(value) == argsClass || false;
+    }
+
+    /**
+     * Checks if `value` is an array.
+     *
+     * @static
+     * @memberOf _
+     * @type Function
+     * @category Objects
+     * @param {*} value The value to check.
+     * @returns {boolean} Returns `true` if the `value` is an array, else `false`.
+     * @example
+     *
+     * (function() { return _.isArray(arguments); })();
+     * // => false
+     *
+     * _.isArray([1, 2, 3]);
+     * // => true
+     */
+    var isArray = nativeIsArray || function(value) {
+      return value && typeof value == 'object' && typeof value.length == 'number' &&
+        toString.call(value) == arrayClass || false;
+    };
+
+    /**
+     * A fallback implementation of `Object.keys` which produces an array of the
+     * given object's own enumerable property names.
+     *
+     * @private
+     * @type Function
+     * @param {Object} object The object to inspect.
+     * @returns {Array} Returns an array of property names.
+     */
+    var shimKeys = function(object) {
+      var index, iterable = object, result = [];
+      if (!iterable) return result;
+      if (!(objectTypes[typeof object])) return result;
+        for (index in iterable) {
+          if (hasOwnProperty.call(iterable, index)) {
+            result.push(index);
+          }
+        }
+      return result
+    };
+
+    /**
+     * Creates an array composed of the own enumerable property names of an object.
+     *
+     * @static
+     * @memberOf _
+     * @category Objects
+     * @param {Object} object The object to inspect.
+     * @returns {Array} Returns an array of property names.
+     * @example
+     *
+     * _.keys({ 'one': 1, 'two': 2, 'three': 3 });
+     * // => ['one', 'two', 'three'] (property order is not guaranteed across environments)
+     */
+    var keys = !nativeKeys ? shimKeys : function(object) {
+      if (!isObject(object)) {
+        return [];
+      }
+      return nativeKeys(object);
+    };
+
+    /**
+     * Used to convert characters to HTML entities:
+     *
+     * Though the `>` character is escaped for symmetry, characters like `>` and `/`
+     * don't require escaping in HTML and have no special meaning unless they're part
+     * of a tag or an unquoted attribute value.
+     * http://mathiasbynens.be/notes/ambiguous-ampersands (under "semi-related fun fact")
+     */
+    var htmlEscapes = {
+      '&': '&amp;',
+      '<': '&lt;',
+      '>': '&gt;',
+      '"': '&quot;',
+      "'": '&#39;'
+    };
+
+    /** Used to convert HTML entities to characters */
+    var htmlUnescapes = invert(htmlEscapes);
+
+    /** Used to match HTML entities and HTML characters */
+    var reEscapedHtml = RegExp('(' + keys(htmlUnescapes).join('|') + ')', 'g'),
+        reUnescapedHtml = RegExp('[' + keys(htmlEscapes).join('') + ']', 'g');
+
+    /*--------------------------------------------------------------------------*/
+
+    /**
+     * Assigns own enumerable properties of source object(s) to the destination
+     * object. Subsequent sources will overwrite property assignments of previous
+     * sources. If a callback is provided it will be executed to produce the
+     * assigned values. The callback is bound to `thisArg` and invoked with two
+     * arguments; (objectValue, sourceValue).
+     *
+     * @static
+     * @memberOf _
+     * @type Function
+     * @alias extend
+     * @category Objects
+     * @param {Object} object The destination object.
+     * @param {...Object} [source] The source objects.
+     * @param {Function} [callback] The function to customize assigning values.
+     * @param {*} [thisArg] The `this` binding of `callback`.
+     * @returns {Object} Returns the destination object.
+     * @example
+     *
+     * _.assign({ 'name': 'fred' }, { 'employer': 'slate' });
+     * // => { 'name': 'fred', 'employer': 'slate' }
+     *
+     * var defaults = _.partialRight(_.assign, function(a, b) {
+     *   return typeof a == 'undefined' ? b : a;
+     * });
+     *
+     * var object = { 'name': 'barney' };
+     * defaults(object, { 'name': 'fred', 'employer': 'slate' });
+     * // => { 'name': 'barney', 'employer': 'slate' }
+     */
+    var assign = function(object, source, guard) {
+      var index, iterable = object, result = iterable;
+      if (!iterable) return result;
+      var args = arguments,
+          argsIndex = 0,
+          argsLength = typeof guard == 'number' ? 2 : args.length;
+      if (argsLength > 3 && typeof args[argsLength - 2] == 'function') {
+        var callback = baseCreateCallback(args[--argsLength - 1], args[argsLength--], 2);
+      } else if (argsLength > 2 && typeof args[argsLength - 1] == 'function') {
+        callback = args[--argsLength];
+      }
+      while (++argsIndex < argsLength) {
+        iterable = args[argsIndex];
+        if (iterable && objectTypes[typeof iterable]) {
+        var ownIndex = -1,
+            ownProps = objectTypes[typeof iterable] && keys(iterable),
+            length = ownProps ? ownProps.length : 0;
+
+        while (++ownIndex < length) {
+          index = ownProps[ownIndex];
+          result[index] = callback ? callback(result[index], iterable[index]) : iterable[index];
+        }
+        }
+      }
+      return result
+    };
+
+    /**
+     * Creates a clone of `value`. If `isDeep` is `true` nested objects will also
+     * be cloned, otherwise they will be assigned by reference. If a callback
+     * is provided it will be executed to produce the cloned values. If the
+     * callback returns `undefined` cloning will be handled by the method instead.
+     * The callback is bound to `thisArg` and invoked with one argument; (value).
+     *
+     * @static
+     * @memberOf _
+     * @category Objects
+     * @param {*} value The value to clone.
+     * @param {boolean} [isDeep=false] Specify a deep clone.
+     * @param {Function} [callback] The function to customize cloning values.
+     * @param {*} [thisArg] The `this` binding of `callback`.
+     * @returns {*} Returns the cloned value.
+     * @example
+     *
+     * var characters = [
+     *   { 'name': 'barney', 'age': 36 },
+     *   { 'name': 'fred',   'age': 40 }
+     * ];
+     *
+     * var shallow = _.clone(characters);
+     * shallow[0] === characters[0];
+     * // => true
+     *
+     * var deep = _.clone(characters, true);
+     * deep[0] === characters[0];
+     * // => false
+     *
+     * _.mixin({
+     *   'clone': _.partialRight(_.clone, function(value) {
+     *     return _.isElement(value) ? value.cloneNode(false) : undefined;
+     *   })
+     * });
+     *
+     * var clone = _.clone(document.body);
+     * clone.childNodes.length;
+     * // => 0
+     */
+    function clone(value, isDeep, callback, thisArg) {
+      // allows working with "Collections" methods without using their `index`
+      // and `collection` arguments for `isDeep` and `callback`
+      if (typeof isDeep != 'boolean' && isDeep != null) {
+        thisArg = callback;
+        callback = isDeep;
+        isDeep = false;
+      }
+      return baseClone(value, isDeep, typeof callback == 'function' && baseCreateCallback(callback, thisArg, 1));
+    }
+
+    /**
+     * Creates a deep clone of `value`. If a callback is provided it will be
+     * executed to produce the cloned values. If the callback returns `undefined`
+     * cloning will be handled by the method instead. The callback is bound to
+     * `thisArg` and invoked with one argument; (value).
+     *
+     * Note: This method is loosely based on the structured clone algorithm. Functions
+     * and DOM nodes are **not** cloned. The enumerable properties of `arguments` objects and
+     * objects created by constructors other than `Object` are cloned to plain `Object` objects.
+     * See http://www.w3.org/TR/html5/infrastructure.html#internal-structured-cloning-algorithm.
+     *
+     * @static
+     * @memberOf _
+     * @category Objects
+     * @param {*} value The value to deep clone.
+     * @param {Function} [callback] The function to customize cloning values.
+     * @param {*} [thisArg] The `this` binding of `callback`.
+     * @returns {*} Returns the deep cloned value.
+     * @example
+     *
+     * var characters = [
+     *   { 'name': 'barney', 'age': 36 },
+     *   { 'name': 'fred',   'age': 40 }
+     * ];
+     *
+     * var deep = _.cloneDeep(characters);
+     * deep[0] === characters[0];
+     * // => false
+     *
+     * var view = {
+     *   'label': 'docs',
+     *   'node': element
+     * };
+     *
+     * var clone = _.cloneDeep(view, function(value) {
+     *   return _.isElement(value) ? value.cloneNode(true) : undefined;
+     * });
+     *
+     * clone.node == view.node;
+     * // => false
+     */
+    function cloneDeep(value, callback, thisArg) {
+      return baseClone(value, true, typeof callback == 'function' && baseCreateCallback(callback, thisArg, 1));
+    }
+
+    /**
+     * Creates an object that inherits from the given `prototype` object. If a
+     * `properties` object is provided its own enumerable properties are assigned
+     * to the created object.
+     *
+     * @static
+     * @memberOf _
+     * @category Objects
+     * @param {Object} prototype The object to inherit from.
+     * @param {Object} [properties] The properties to assign to the object.
+     * @returns {Object} Returns the new object.
+     * @example
+     *
+     * function Shape() {
+     *   this.x = 0;
+     *   this.y = 0;
+     * }
+     *
+     * function Circle() {
+     *   Shape.call(this);
+     * }
+     *
+     * Circle.prototype = _.create(Shape.prototype, { 'constructor': Circle });
+     *
+     * var circle = new Circle;
+     * circle instanceof Circle;
+     * // => true
+     *
+     * circle instanceof Shape;
+     * // => true
+     */
+    function create(prototype, properties) {
+      var result = baseCreate(prototype);
+      return properties ? assign(result, properties) : result;
+    }
+
+    /**
+     * Assigns own enumerable properties of source object(s) to the destination
+     * object for all destination properties that resolve to `undefined`. Once a
+     * property is set, additional defaults of the same property will be ignored.
+     *
+     * @static
+     * @memberOf _
+     * @type Function
+     * @category Objects
+     * @param {Object} object The destination object.
+     * @param {...Object} [source] The source objects.
+     * @param- {Object} [guard] Allows working with `_.reduce` without using its
+     *  `key` and `object` arguments as sources.
+     * @returns {Object} Returns the destination object.
+     * @example
+     *
+     * var object = { 'name': 'barney' };
+     * _.defaults(object, { 'name': 'fred', 'employer': 'slate' });
+     * // => { 'name': 'barney', 'employer': 'slate' }
+     */
+    var defaults = function(object, source, guard) {
+      var index, iterable = object, result = iterable;
+      if (!iterable) return result;
+      var args = arguments,
+          argsIndex = 0,
+          argsLength = typeof guard == 'number' ? 2 : args.length;
+      while (++argsIndex < argsLength) {
+        iterable = args[argsIndex];
+        if (iterable && objectTypes[typeof iterable]) {
+        var ownIndex = -1,
+            ownProps = objectTypes[typeof iterable] && keys(iterable),
+            length = ownProps ? ownProps.length : 0;
+
+        while (++ownIndex < length) {
+          index = ownProps[ownIndex];
+          if (typeof result[index] == 'undefined') result[index] = iterable[index];
+        }
+        }
+      }
+      return result
+    };
+
+    /**
+     * This method is like `_.findIndex` except that it returns the key of the
+     * first element that passes the callback check, instead of the element itself.
+     *
+     * If a property name is provided for `callback` the created "_.pluck" style
+     * callback will return the property value of the given element.
+     *
+     * If an object is provided for `callback` the created "_.where" style callback
+     * will return `true` for elements that have the properties of the given object,
+     * else `false`.
+     *
+     * @static
+     * @memberOf _
+     * @category Objects
+     * @param {Object} object The object to search.
+     * @param {Function|Object|string} [callback=identity] The function called per
+     *  iteration. If a property name or object is provided it will be used to
+     *  create a "_.pluck" or "_.where" style callback, respectively.
+     * @param {*} [thisArg] The `this` binding of `callback`.
+     * @returns {string|undefined} Returns the key of the found element, else `undefined`.
+     * @example
+     *
+     * var characters = {
+     *   'barney': {  'age': 36, 'blocked': false },
+     *   'fred': {    'age': 40, 'blocked': true },
+     *   'pebbles': { 'age': 1,  'blocked': false }
+     * };
+     *
+     * _.findKey(characters, function(chr) {
+     *   return chr.age < 40;
+     * });
+     * // => 'barney' (property order is not guaranteed across environments)
+     *
+     * // using "_.where" callback shorthand
+     * _.findKey(characters, { 'age': 1 });
+     * // => 'pebbles'
+     *
+     * // using "_.pluck" callback shorthand
+     * _.findKey(characters, 'blocked');
+     * // => 'fred'
+     */
+    function findKey(object, callback, thisArg) {
+      var result;
+      callback = lodash.createCallback(callback, thisArg, 3);
+      forOwn(object, function(value, key, object) {
+        if (callback(value, key, object)) {
+          result = key;
+          return false;
+        }
+      });
+      return result;
+    }
+
+    /**
+     * This method is like `_.findKey` except that it iterates over elements
+     * of a `collection` in the opposite order.
+     *
+     * If a property name is provided for `callback` the created "_.pluck" style
+     * callback will return the property value of the given element.
+     *
+     * If an object is provided for `callback` the created "_.where" style callback
+     * will return `true` for elements that have the properties of the given object,
+     * else `false`.
+     *
+     * @static
+     * @memberOf _
+     * @category Objects
+     * @param {Object} object The object to search.
+     * @param {Function|Object|string} [callback=identity] The function called per
+     *  iteration. If a property name or object is provided it will be used to
+     *  create a "_.pluck" or "_.where" style callback, respectively.
+     * @param {*} [thisArg] The `this` binding of `callback`.
+     * @returns {string|undefined} Returns the key of the found element, else `undefined`.
+     * @example
+     *
+     * var characters = {
+     *   'barney': {  'age': 36, 'blocked': true },
+     *   'fred': {    'age': 40, 'blocked': false },
+     *   'pebbles': { 'age': 1,  'blocked': true }
+     * };
+     *
+     * _.findLastKey(characters, function(chr) {
+     *   return chr.age < 40;
+     * });
+     * // => returns `pebbles`, assuming `_.findKey` returns `barney`
+     *
+     * // using "_.where" callback shorthand
+     * _.findLastKey(characters, { 'age': 40 });
+     * // => 'fred'
+     *
+     * // using "_.pluck" callback shorthand
+     * _.findLastKey(characters, 'blocked');
+     * // => 'pebbles'
+     */
+    function findLastKey(object, callback, thisArg) {
+      var result;
+      callback = lodash.createCallback(callback, thisArg, 3);
+      forOwnRight(object, function(value, key, object) {
+        if (callback(value, key, object)) {
+          result = key;
+          return false;
+        }
+      });
+      return result;
+    }
+
+    /**
+     * Iterates over own and inherited enumerable properties of an object,
+     * executing the callback for each property. The callback is bound to `thisArg`
+     * and invoked with three arguments; (value, key, object). Callbacks may exit
+     * iteration early by explicitly returning `false`.
+     *
+     * @static
+     * @memberOf _
+     * @type Function
+     * @category Objects
+     * @param {Object} object The object to iterate over.
+     * @param {Function} [callback=identity] The function called per iteration.
+     * @param {*} [thisArg] The `this` binding of `callback`.
+     * @returns {Object} Returns `object`.
+     * @example
+     *
+     * function Shape() {
+     *   this.x = 0;
+     *   this.y = 0;
+     * }
+     *
+     * Shape.prototype.move = function(x, y) {
+     *   this.x += x;
+     *   this.y += y;
+     * };
+     *
+     * _.forIn(new Shape, function(value, key) {
+     *   console.log(key);
+     * });
+     * // => logs 'x', 'y', and 'move' (property order is not guaranteed across environments)
+     */
+    var forIn = function(collection, callback, thisArg) {
+      var index, iterable = collection, result = iterable;
+      if (!iterable) return result;
+      if (!objectTypes[typeof iterable]) return result;
+      callback = callback && typeof thisArg == 'undefined' ? callback : baseCreateCallback(callback, thisArg, 3);
+        for (index in iterable) {
+          if (callback(iterable[index], index, collection) === false) return result;
+        }
+      return result
+    };
+
+    /**
+     * This method is like `_.forIn` except that it iterates over elements
+     * of a `collection` in the opposite order.
+     *
+     * @static
+     * @memberOf _
+     * @category Objects
+     * @param {Object} object The object to iterate over.
+     * @param {Function} [callback=identity] The function called per iteration.
+     * @param {*} [thisArg] The `this` binding of `callback`.
+     * @returns {Object} Returns `object`.
+     * @example
+     *
+     * function Shape() {
+     *   this.x = 0;
+     *   this.y = 0;
+     * }
+     *
+     * Shape.prototype.move = function(x, y) {
+     *   this.x += x;
+     *   this.y += y;
+     * };
+     *
+     * _.forInRight(new Shape, function(value, key) {
+     *   console.log(key);
+     * });
+     * // => logs 'move', 'y', and 'x' assuming `_.forIn ` logs 'x', 'y', and 'move'
+     */
+    function forInRight(object, callback, thisArg) {
+      var pairs = [];
+
+      forIn(object, function(value, key) {
+        pairs.push(key, value);
+      });
+
+      var length = pairs.length;
+      callback = baseCreateCallback(callback, thisArg, 3);
+      while (length--) {
+        if (callback(pairs[length--], pairs[length], object) === false) {
+          break;
+        }
+      }
+      return object;
+    }
+
+    /**
+     * Iterates over own enumerable properties of an object, executing the callback
+     * for each property. The callback is bound to `thisArg` and invoked with three
+     * arguments; (value, key, object). Callbacks may exit iteration early by
+     * explicitly returning `false`.
+     *
+     * @static
+     * @memberOf _
+     * @type Function
+     * @category Objects
+     * @param {Object} object The object to iterate over.
+     * @param {Function} [callback=identity] The function called per iteration.
+     * @param {*} [thisArg] The `this` binding of `callback`.
+     * @returns {Object} Returns `object`.
+     * @example
+     *
+     * _.forOwn({ '0': 'zero', '1': 'one', 'length': 2 }, function(num, key) {
+     *   console.log(key);
+     * });
+     * // => logs '0', '1', and 'length' (property order is not guaranteed across environments)
+     */
+    var forOwn = function(collection, callback, thisArg) {
+      var index, iterable = collection, result = iterable;
+      if (!iterable) return result;
+      if (!objectTypes[typeof iterable]) return result;
+      callback = callback && typeof thisArg == 'undefined' ? callback : baseCreateCallback(callback, thisArg, 3);
+        var ownIndex = -1,
+            ownProps = objectTypes[typeof iterable] && keys(iterable),
+            length = ownProps ? ownProps.length : 0;
+
+        while (++ownIndex < length) {
+          index = ownProps[ownIndex];
+          if (callback(iterable[index], index, collection) === false) return result;
+        }
+      return result
+    };
+
+    /**
+     * This method is like `_.forOwn` except that it iterates over elements
+     * of a `collection` in the opposite order.
+     *
+     * @static
+     * @memberOf _
+     * @category Objects
+     * @param {Object} object The object to iterate over.
+     * @param {Function} [callback=identity] The function called per iteration.
+     * @param {*} [thisArg] The `this` binding of `callback`.
+     * @returns {Object} Returns `object`.
+     * @example
+     *
+     * _.forOwnRight({ '0': 'zero', '1': 'one', 'length': 2 }, function(num, key) {
+     *   console.log(key);
+     * });
+     * // => logs 'length', '1', and '0' assuming `_.forOwn` logs '0', '1', and 'length'
+     */
+    function forOwnRight(object, callback, thisArg) {
+      var props = keys(object),
+          length = props.length;
+
+      callback = baseCreateCallback(callback, thisArg, 3);
+      while (length--) {
+        var key = props[length];
+        if (callback(object[key], key, object) === false) {
+          break;
+        }
+      }
+      return object;
+    }
+
+    /**
+     * Creates a sorted array of property names of all enumerable properties,
+     * own and inherited, of `object` that have function values.
+     *
+     * @static
+     * @memberOf _
+     * @alias methods
+     * @category Objects
+     * @param {Object} object The object to inspect.
+     * @returns {Array} Returns an array of property names that have function values.
+     * @example
+     *
+     * _.functions(_);
+     * // => ['all', 'any', 'bind', 'bindAll', 'clone', 'compact', 'compose', ...]
+     */
+    function functions(object) {
+      var result = [];
+      forIn(object, function(value, key) {
+        if (isFunction(value)) {
+          result.push(key);
+        }
+      });
+      return result.sort();
+    }
+
+    /**
+     * Checks if the specified property name exists as a direct property of `object`,
+     * instead of an inherited property.
+     *
+     * @static
+     * @memberOf _
+     * @category Objects
+     * @param {Object} object The object to inspect.
+     * @param {string} key The name of the property to check.
+     * @returns {boolean} Returns `true` if key is a direct property, else `false`.
+     * @example
+     *
+     * _.has({ 'a': 1, 'b': 2, 'c': 3 }, 'b');
+     * // => true
+     */
+    function has(object, key) {
+      return object ? hasOwnProperty.call(object, key) : false;
+    }
+
+    /**
+     * Creates an object composed of the inverted keys and values of the given object.
+     *
+     * @static
+     * @memberOf _
+     * @category Objects
+     * @param {Object} object The object to invert.
+     * @returns {Object} Returns the created inverted object.
+     * @example
+     *
+     * _.invert({ 'first': 'fred', 'second': 'barney' });
+     * // => { 'fred': 'first', 'barney': 'second' }
+     */
+    function invert(object) {
+      var index = -1,
+          props = keys(object),
+          length = props.length,
+          result = {};
+
+      while (++index < length) {
+        var key = props[index];
+        result[object[key]] = key;
+      }
+      return result;
+    }
+
+    /**
+     * Checks if `value` is a boolean value.
+     *
+     * @static
+     * @memberOf _
+     * @category Objects
+     * @param {*} value The value to check.
+     * @returns {boolean} Returns `true` if the `value` is a boolean value, else `false`.
+     * @example
+     *
+     * _.isBoolean(null);
+     * // => false
+     */
+    function isBoolean(value) {
+      return value === true || value === false ||
+        value && typeof value == 'object' && toString.call(value) == boolClass || false;
+    }
+
+    /**
+     * Checks if `value` is a date.
+     *
+     * @static
+     * @memberOf _
+     * @category Objects
+     * @param {*} value The value to check.
+     * @returns {boolean} Returns `true` if the `value` is a date, else `false`.
+     * @example
+     *
+     * _.isDate(new Date);
+     * // => true
+     */
+    function isDate(value) {
+      return value && typeof value == 'object' && toString.call(value) == dateClass || false;
+    }
+
+    /**
+     * Checks if `value` is a DOM element.
+     *
+     * @static
+     * @memberOf _
+     * @category Objects
+     * @param {*} value The value to check.
+     * @returns {boolean} Returns `true` if the `value` is a DOM element, else `false`.
+     * @example
+     *
+     * _.isElement(document.body);
+     * // => true
+     */
+    function isElement(value) {
+      return value && value.nodeType === 1 || false;
+    }
+
+    /**
+     * Checks if `value` is empty. Arrays, strings, or `arguments` objects with a
+     * length of `0` and objects with no own enumerable properties are considered
+     * "empty".
+     *
+     * @static
+     * @memberOf _
+     * @category Objects
+     * @param {Array|Object|string} value The value to inspect.
+     * @returns {boolean} Returns `true` if the `value` is empty, else `false`.
+     * @example
+     *
+     * _.isEmpty([1, 2, 3]);
+     * // => false
+     *
+     * _.isEmpty({});
+     * // => true
+     *
+     * _.isEmpty('');
+     * // => true
+     */
+    function isEmpty(value) {
+      var result = true;
+      if (!value) {
+        return result;
+      }
+      var className = toString.call(value),
+          length = value.length;
+
+      if ((className == arrayClass || className == stringClass || className == argsClass ) ||
+          (className == objectClass && typeof length == 'number' && isFunction(value.splice))) {
+        return !length;
+      }
+      forOwn(value, function() {
+        return (result = false);
+      });
+      return result;
+    }
+
+    /**
+     * Performs a deep comparison between two values to determine if they are
+     * equivalent to each other. If a callback is provided it will be executed
+     * to compare values. If the callback returns `undefined` comparisons will
+     * be handled by the method instead. The callback is bound to `thisArg` and
+     * invoked with two arguments; (a, b).
+     *
+     * @static
+     * @memberOf _
+     * @category Objects
+     * @param {*} a The value to compare.
+     * @param {*} b The other value to compare.
+     * @param {Function} [callback] The function to customize comparing values.
+     * @param {*} [thisArg] The `this` binding of `callback`.
+     * @returns {boolean} Returns `true` if the values are equivalent, else `false`.
+     * @example
+     *
+     * var object = { 'name': 'fred' };
+     * var copy = { 'name': 'fred' };
+     *
+     * object == copy;
+     * // => false
+     *
+     * _.isEqual(object, copy);
+     * // => true
+     *
+     * var words = ['hello', 'goodbye'];
+     * var otherWords = ['hi', 'goodbye'];
+     *
+     * _.isEqual(words, otherWords, function(a, b) {
+     *   var reGreet = /^(?:hello|hi)$/i,
+     *       aGreet = _.isString(a) && reGreet.test(a),
+     *       bGreet = _.isString(b) && reGreet.test(b);
+     *
+     *   return (aGreet || bGreet) ? (aGreet == bGreet) : undefined;
+     * });
+     * // => true
+     */
+    function isEqual(a, b, callback, thisArg) {
+      return baseIsEqual(a, b, typeof callback == 'function' && baseCreateCallback(callback, thisArg, 2));
+    }
+
+    /**
+     * Checks if `value` is, or can be coerced to, a finite number.
+     *
+     * Note: This is not the same as native `isFinite` which will return true for
+     * booleans and empty strings. See http://es5.github.io/#x15.1.2.5.
+     *
+     * @static
+     * @memberOf _
+     * @category Objects
+     * @param {*} value The value to check.
+     * @returns {boolean} Returns `true` if the `value` is finite, else `false`.
+     * @example
+     *
+     * _.isFinite(-101);
+     * // => true
+     *
+     * _.isFinite('10');
+     * // => true
+     *
+     * _.isFinite(true);
+     * // => false
+     *
+     * _.isFinite('');
+     * // => false
+     *
+     * _.isFinite(Infinity);
+     * // => false
+     */
+    function isFinite(value) {
+      return nativeIsFinite(value) && !nativeIsNaN(parseFloat(value));
+    }
+
+    /**
+     * Checks if `value` is a function.
+     *
+     * @static
+     * @memberOf _
+     * @category Objects
+     * @param {*} value The value to check.
+     * @returns {boolean} Returns `true` if the `value` is a function, else `false`.
+     * @example
+     *
+     * _.isFunction(_);
+     * // => true
+     */
+    function isFunction(value) {
+      return typeof value == 'function';
+    }
+
+    /**
+     * Checks if `value` is the language type of Object.
+     * (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)
+     *
+     * @static
+     * @memberOf _
+     * @category Objects
+     * @param {*} value The value to check.
+     * @returns {boolean} Returns `true` if the `value` is an object, else `false`.
+     * @example
+     *
+     * _.isObject({});
+     * // => true
+     *
+     * _.isObject([1, 2, 3]);
+     * // => true
+     *
+     * _.isObject(1);
+     * // => false
+     */
+    function isObject(value) {
+      // check if the value is the ECMAScript language type of Object
+      // http://es5.github.io/#x8
+      // and avoid a V8 bug
+      // http://code.google.com/p/v8/issues/detail?id=2291
+      return !!(value && objectTypes[typeof value]);
+    }
+
+    /**
+     * Checks if `value` is `NaN`.
+     *
+     * Note: This is not the same as native `isNaN` which will return `true` for
+     * `undefined` and other non-numeric values. See http://es5.github.io/#x15.1.2.4.
+     *
+     * @static
+     * @memberOf _
+     * @category Objects
+     * @param {*} value The value to check.
+     * @returns {boolean} Returns `true` if the `value` is `NaN`, else `false`.
+     * @example
+     *
+     * _.isNaN(NaN);
+     * // => true
+     *
+     * _.isNaN(new Number(NaN));
+     * // => true
+     *
+     * isNaN(undefined);
+     * // => true
+     *
+     * _.isNaN(undefined);
+     * // => false
+     */
+    function isNaN(value) {
+      // `NaN` as a primitive is the only value that is not equal to itself
+      // (perform the [[Class]] check first to avoid errors with some host objects in IE)
+      return isNumber(value) && value != +value;
+    }
+
+    /**
+     * Checks if `value` is `null`.
+     *
+     * @static
+     * @memberOf _
+     * @category Objects
+     * @param {*} value The value to check.
+     * @returns {boolean} Returns `true` if the `value` is `null`, else `false`.
+     * @example
+     *
+     * _.isNull(null);
+     * // => true
+     *
+     * _.isNull(undefined);
+     * // => false
+     */
+    function isNull(value) {
+      return value === null;
+    }
+
+    /**
+     * Checks if `value` is a number.
+     *
+     * Note: `NaN` is considered a number. See http://es5.github.io/#x8.5.
+     *
+     * @static
+     * @memberOf _
+     * @category Objects
+     * @param {*} value The value to check.
+     * @returns {boolean} Returns `true` if the `value` is a number, else `false`.
+     * @example
+     *
+     * _.isNumber(8.4 * 5);
+     * // => true
+     */
+    function isNumber(value) {
+      return typeof value == 'number' ||
+        value && typeof value == 'object' && toString.call(value) == numberClass || false;
+    }
+
+    /**
+     * Checks if `value` is an object created by the `Object` constructor.
+     *
+     * @static
+     * @memberOf _
+     * @category Objects
+     * @param {*} value The value to check.
+     * @returns {boolean} Returns `true` if `value` is a plain object, else `false`.
+     * @example
+     *
+     * function Shape() {
+     *   this.x = 0;
+     *   this.y = 0;
+     * }
+     *
+     * _.isPlainObject(new Shape);
+     * // => false
+     *
+     * _.isPlainObject([1, 2, 3]);
+     * // => false
+     *
+     * _.isPlainObject({ 'x': 0, 'y': 0 });
+     * // => true
+     */
+    var isPlainObject = !getPrototypeOf ? shimIsPlainObject : function(value) {
+      if (!(value && toString.call(value) == objectClass)) {
+        return false;
+      }
+      var valueOf = value.valueOf,
+          objProto = isNative(valueOf) && (objProto = getPrototypeOf(valueOf)) && getPrototypeOf(objProto);
+
+      return objProto
+        ? (value == objProto || getPrototypeOf(value) == objProto)
+        : shimIsPlainObject(value);
+    };
+
+    /**
+     * Checks if `value` is a regular expression.
+     *
+     * @static
+     * @memberOf _
+     * @category Objects
+     * @param {*} value The value to check.
+     * @returns {boolean} Returns `true` if the `value` is a regular expression, else `false`.
+     * @example
+     *
+     * _.isRegExp(/fred/);
+     * // => true
+     */
+    function isRegExp(value) {
+      return value && typeof value == 'object' && toString.call(value) == regexpClass || false;
+    }
+
+    /**
+     * Checks if `value` is a string.
+     *
+     * @static
+     * @memberOf _
+     * @category Objects
+     * @param {*} value The value to check.
+     * @returns {boolean} Returns `true` if the `value` is a string, else `false`.
+     * @example
+     *
+     * _.isString('fred');
+     * // => true
+     */
+    function isString(value) {
+      return typeof value == 'string' ||
+        value && typeof value == 'object' && toString.call(value) == stringClass || false;
+    }
+
+    /**
+     * Checks if `value` is `undefined`.
+     *
+     * @static
+     * @memberOf _
+     * @category Objects
+     * @param {*} value The value to check.
+     * @returns {boolean} Returns `true` if the `value` is `undefined`, else `false`.
+     * @example
+     *
+     * _.isUndefined(void 0);
+     * // => true
+     */
+    function isUndefined(value) {
+      return typeof value == 'undefined';
+    }
+
+    /**
+     * Creates an object with the same keys as `object` and values generated by
+     * running each own enumerable property of `object` through the callback.
+     * The callback is bound to `thisArg` and invoked with three arguments;
+     * (value, key, object).
+     *
+     * If a property name is provided for `callback` the created "_.pluck" style
+     * callback will return the property value of the given element.
+     *
+     * If an object is provided for `callback` the created "_.where" style callback
+     * will return `true` for elements that have the properties of the given object,
+     * else `false`.
+     *
+     * @static
+     * @memberOf _
+     * @category Objects
+     * @param {Object} object The object to iterate over.
+     * @param {Function|Object|string} [callback=identity] The function called
+     *  per iteration. If a property name or object is provided it will be used
+     *  to create a "_.pluck" or "_.where" style callback, respectively.
+     * @param {*} [thisArg] The `this` binding of `callback`.
+     * @returns {Array} Returns a new object with values of the results of each `callback` execution.
+     * @example
+     *
+     * _.mapValues({ 'a': 1, 'b': 2, 'c': 3} , function(num) { return num * 3; });
+     * // => { 'a': 3, 'b': 6, 'c': 9 }
+     *
+     * var characters = {
+     *   'fred': { 'name': 'fred', 'age': 40 },
+     *   'pebbles': { 'name': 'pebbles', 'age': 1 }
+     * };
+     *
+     * // using "_.pluck" callback shorthand
+     * _.mapValues(characters, 'age');
+     * // => { 'fred': 40, 'pebbles': 1 }
+     */
+    function mapValues(object, callback, thisArg) {
+      var result = {};
+      callback = lodash.createCallback(callback, thisArg, 3);
+
+      forOwn(object, function(value, key, object) {
+        result[key] = callback(value, key, object);
+      });
+      return result;
+    }
+
+    /**
+     * Recursively merges own enumerable properties of the source object(s), that
+     * don't resolve to `undefined` into the destination object. Subsequent sources
+     * will overwrite property assignments of previous sources. If a callback is
+     * provided it will be executed to produce the merged values of the destination
+     * and source properties. If the callback returns `undefined` merging will
+     * be handled by the method instead. The callback is bound to `thisArg` and
+     * invoked with two arguments; (objectValue, sourceValue).
+     *
+     * @static
+     * @memberOf _
+     * @category Objects
+     * @param {Object} object The destination object.
+     * @param {...Object} [source] The source objects.
+     * @param {Function} [callback] The function to customize merging properties.
+     * @param {*} [thisArg] The `this` binding of `callback`.
+     * @returns {Object} Returns the destination object.
+     * @example
+     *
+     * var names = {
+     *   'characters': [
+     *     { 'name': 'barney' },
+     *     { 'name': 'fred' }
+     *   ]
+     * };
+     *
+     * var ages = {
+     *   'characters': [
+     *     { 'age': 36 },
+     *     { 'age': 40 }
+     *   ]
+     * };
+     *
+     * _.merge(names, ages);
+     * // => { 'characters': [{ 'name': 'barney', 'age': 36 }, { 'name': 'fred', 'age': 40 }] }
+     *
+     * var food = {
+     *   'fruits': ['apple'],
+     *   'vegetables': ['beet']
+     * };
+     *
+     * var otherFood = {
+     *   'fruits': ['banana'],
+     *   'vegetables': ['carrot']
+     * };
+     *
+     * _.merge(food, otherFood, function(a, b) {
+     *   return _.isArray(a) ? a.concat(b) : undefined;
+     * });
+     * // => { 'fruits': ['apple', 'banana'], 'vegetables': ['beet', 'carrot] }
+     */
+    function merge(object) {
+      var args = arguments,
+          length = 2;
+
+      if (!isObject(object)) {
+        return object;
+      }
+      // allows working with `_.reduce` and `_.reduceRight` without using
+      // their `index` and `collection` arguments
+      if (typeof args[2] != 'number') {
+        length = args.length;
+      }
+      if (length > 3 && typeof args[length - 2] == 'function') {
+        var callback = baseCreateCallback(args[--length - 1], args[length--], 2);
+      } else if (length > 2 && typeof args[length - 1] == 'function') {
+        callback = args[--length];
+      }
+      var sources = slice(arguments, 1, length),
+          index = -1,
+          stackA = getArray(),
+          stackB = getArray();
+
+      while (++index < length) {
+        baseMerge(object, sources[index], callback, stackA, stackB);
+      }
+      releaseArray(stackA);
+      releaseArray(stackB);
+      return object;
+    }
+
+    /**
+     * Creates a shallow clone of `object` excluding the specified properties.
+     * Property names may be specified as individual arguments or as arrays of
+     * property names. If a callback is provided it will be executed for each
+     * property of `object` omitting the properties the callback returns truey
+     * for. The callback is bound to `thisArg` and invoked with three arguments;
+     * (value, key, object).
+     *
+     * @static
+     * @memberOf _
+     * @category Objects
+     * @param {Object} object The source object.
+     * @param {Function|...string|string[]} [callback] The properties to omit or the
+     *  function called per iteration.
+     * @param {*} [thisArg] The `this` binding of `callback`.
+     * @returns {Object} Returns an object without the omitted properties.
+     * @example
+     *
+     * _.omit({ 'name': 'fred', 'age': 40 }, 'age');
+     * // => { 'name': 'fred' }
+     *
+     * _.omit({ 'name': 'fred', 'age': 40 }, function(value) {
+     *   return typeof value == 'number';
+     * });
+     * // => { 'name': 'fred' }
+     */
+    function omit(object, callback, thisArg) {
+      var result = {};
+      if (typeof callback != 'function') {
+        var props = [];
+        forIn(object, function(value, key) {
+          props.push(key);
+        });
+        props = baseDifference(props, baseFlatten(arguments, true, false, 1));
+
+        var index = -1,
+            length = props.length;
+
+        while (++index < length) {
+          var key = props[index];
+          result[key] = object[key];
+        }
+      } else {
+        callback = lodash.createCallback(callback, thisArg, 3);
+        forIn(object, function(value, key, object) {
+          if (!callback(value, key, object)) {
+            result[key] = value;
+          }
+        });
+      }
+      return result;
+    }
+
+    /**
+     * Creates a two dimensional array of an object's key-value pairs,
+     * i.e. `[[key1, value1], [key2, value2]]`.
+     *
+     * @static
+     * @memberOf _
+     * @category Objects
+     * @param {Object} object The object to inspect.
+     * @returns {Array} Returns new array of key-value pairs.
+     * @example
+     *
+     * _.pairs({ 'barney': 36, 'fred': 40 });
+     * // => [['barney', 36], ['fred', 40]] (property order is not guaranteed across environments)
+     */
+    function pairs(object) {
+      var index = -1,
+          props = keys(object),
+          length = props.length,
+          result = Array(length);
+
+      while (++index < length) {
+        var key = props[index];
+        result[index] = [key, object[key]];
+      }
+      return result;
+    }
+
+    /**
+     * Creates a shallow clone of `object` composed of the specified properties.
+     * Property names may be specified as individual arguments or as arrays of
+     * property names. If a callback is provided it will be executed for each
+     * property of `object` picking the properties the callback returns truey
+     * for. The callback is bound to `thisArg` and invoked with three arguments;
+     * (value, key, object).
+     *
+     * @static
+     * @memberOf _
+     * @category Objects
+     * @param {Object} object The source object.
+     * @param {Function|...string|string[]} [callback] The function called per
+     *  iteration or property names to pick, specified as individual property
+     *  names or arrays of property names.
+     * @param {*} [thisArg] The `this` binding of `callback`.
+     * @returns {Object} Returns an object composed of the picked properties.
+     * @example
+     *
+     * _.pick({ 'name': 'fred', '_userid': 'fred1' }, 'name');
+     * // => { 'name': 'fred' }
+     *
+     * _.pick({ 'name': 'fred', '_userid': 'fred1' }, function(value, key) {
+     *   return key.charAt(0) != '_';
+     * });
+     * // => { 'name': 'fred' }
+     */
+    function pick(object, callback, thisArg) {
+      var result = {};
+      if (typeof callback != 'function') {
+        var index = -1,
+            props = baseFlatten(arguments, true, false, 1),
+            length = isObject(object) ? props.length : 0;
+
+        while (++index < length) {
+          var key = props[index];
+          if (key in object) {
+            result[key] = object[key];
+          }
+        }
+      } else {
+        callback = lodash.createCallback(callback, thisArg, 3);
+        forIn(object, function(value, key, object) {
+          if (callback(value, key, object)) {
+            result[key] = value;
+          }
+        });
+      }
+      return result;
+    }
+
+    /**
+     * An alternative to `_.reduce` this method transforms `object` to a new
+     * `accumulator` object which is the result of running each of its own
+     * enumerable properties through a callback, with each callback execution
+     * potentially mutating the `accumulator` object. The callback is bound to
+     * `thisArg` and invoked with four arguments; (accumulator, value, key, object).
+     * Callbacks may exit iteration early by explicitly returning `false`.
+     *
+     * @static
+     * @memberOf _
+     * @category Objects
+     * @param {Array|Object} object The object to iterate over.
+     * @param {Function} [callback=identity] The function called per iteration.
+     * @param {*} [accumulator] The custom accumulator value.
+     * @param {*} [thisArg] The `this` binding of `callback`.
+     * @returns {*} Returns the accumulated value.
+     * @example
+     *
+     * var squares = _.transform([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], function(result, num) {
+     *   num *= num;
+     *   if (num % 2) {
+     *     return result.push(num) < 3;
+     *   }
+     * });
+     * // => [1, 9, 25]
+     *
+     * var mapped = _.transform({ 'a': 1, 'b': 2, 'c': 3 }, function(result, num, key) {
+     *   result[key] = num * 3;
+     * });
+     * // => { 'a': 3, 'b': 6, 'c': 9 }
+     */
+    function transform(object, callback, accumulator, thisArg) {
+      var isArr = isArray(object);
+      if (accumulator == null) {
+        if (isArr) {
+          accumulator = [];
+        } else {
+          var ctor = object && object.constructor,
+              proto = ctor && ctor.prototype;
+
+          accumulator = baseCreate(proto);
+        }
+      }
+      if (callback) {
+        callback = lodash.createCallback(callback, thisArg, 4);
+        (isArr ? forEach : forOwn)(object, function(value, index, object) {
+          return callback(accumulator, value, index, object);
+        });
+      }
+      return accumulator;
+    }
+
+    /**
+     * Creates an array composed of the own enumerable property values of `object`.
+     *
+     * @static
+     * @memberOf _
+     * @category Objects
+     * @param {Object} object The object to inspect.
+     * @returns {Array} Returns an array of property values.
+     * @example
+     *
+     * _.values({ 'one': 1, 'two': 2, 'three': 3 });
+     * // => [1, 2, 3] (property order is not guaranteed across environments)
+     */
+    function values(object) {
+      var index = -1,
+          props = keys(object),
+          length = props.length,
+          result = Array(length);
+
+      while (++index < length) {
+        result[index] = object[props[index]];
+      }
+      return result;
+    }
+
+    /*--------------------------------------------------------------------------*/
+
+    /**
+     * Creates an array of elements from the specified indexes, or keys, of the
+     * `collection`. Indexes may be specified as individual arguments or as arrays
+     * of indexes.
+     *
+     * @static
+     * @memberOf _
+     * @category Collections
+     * @param {Array|Object|string} collection The collection to iterate over.
+     * @param {...(number|number[]|string|string[])} [index] The indexes of `collection`
+     *   to retrieve, specified as individual indexes or arrays of indexes.
+     * @returns {Array} Returns a new array of elements corresponding to the
+     *  provided indexes.
+     * @example
+     *
+     * _.at(['a', 'b', 'c', 'd', 'e'], [0, 2, 4]);
+     * // => ['a', 'c', 'e']
+     *
+     * _.at(['fred', 'barney', 'pebbles'], 0, 2);
+     * // => ['fred', 'pebbles']
+     */
+    function at(collection) {
+      var args = arguments,
+          index = -1,
+          props = baseFlatten(args, true, false, 1),
+          length = (args[2] && args[2][args[1]] === collection) ? 1 : props.length,
+          result = Array(length);
+
+      while(++index < length) {
+        result[index] = collection[props[index]];
+      }
+      return result;
+    }
+
+    /**
+     * Checks if a given value is present in a collection using strict equality
+     * for comparisons, i.e. `===`. If `fromIndex` is negative, it is used as the
+     * offset from the end of the collection.
+     *
+     * @static
+     * @memberOf _
+     * @alias include
+     * @category Collections
+     * @param {Array|Object|string} collection The collection to iterate over.
+     * @param {*} target The value to check for.
+     * @param {number} [fromIndex=0] The index to search from.
+     * @returns {boolean} Returns `true` if the `target` element is found, else `false`.
+     * @example
+     *
+     * _.contains([1, 2, 3], 1);
+     * // => true
+     *
+     * _.contains([1, 2, 3], 1, 2);
+     * // => false
+     *
+     * _.contains({ 'name': 'fred', 'age': 40 }, 'fred');
+     * // => true
+     *
+     * _.contains('pebbles', 'eb');
+     * // => true
+     */
+    function contains(collection, target, fromIndex) {
+      var index = -1,
+          indexOf = getIndexOf(),
+          length = collection ? collection.length : 0,
+          result = false;
+
+      fromIndex = (fromIndex < 0 ? nativeMax(0, length + fromIndex) : fromIndex) || 0;
+      if (isArray(collection)) {
+        result = indexOf(collection, target, fromIndex) > -1;
+      } else if (typeof length == 'number') {
+        result = (isString(collection) ? collection.indexOf(target, fromIndex) : indexOf(collection, target, fromIndex)) > -1;
+      } else {
+        forOwn(collection, function(value) {
+          if (++index >= fromIndex) {
+            return !(result = value === target);
+          }
+        });
+      }
+      return result;
+    }
+
+    /**
+     * Creates an object composed of keys generated from the results of running
+     * each element of `collection` through the callback. The corresponding value
+     * of each key is the number of times the key was returned by the callback.
+     * The callback is bound to `thisArg` and invoked with three arguments;
+     * (value, index|key, collection).
+     *
+     * If a property name is provided for `callback` the created "_.pluck" style
+     * callback will return the property value of the given element.
+     *
+     * If an object is provided for `callback` the created "_.where" style callback
+     * will return `true` for elements that have the properties of the given object,
+     * else `false`.
+     *
+     * @static
+     * @memberOf _
+     * @category Collections
+     * @param {Array|Object|string} collection The collection to iterate over.
+     * @param {Function|Object|string} [callback=identity] The function called
+     *  per iteration. If a property name or object is provided it will be used
+     *  to create a "_.pluck" or "_.where" style callback, respectively.
+     * @param {*} [thisArg] The `this` binding of `callback`.
+     * @returns {Object} Returns the composed aggregate object.
+     * @example
+     *
+     * _.countBy([4.3, 6.1, 6.4], function(num) { return Math.floor(num); });
+     * // => { '4': 1, '6': 2 }
+     *
+     * _.countBy([4.3, 6.1, 6.4], function(num) { return this.floor(num); }, Math);
+     * // => { '4': 1, '6': 2 }
+     *
+     * _.countBy(['one', 'two', 'three'], 'length');
+     * // => { '3': 2, '5': 1 }
+     */
+    var countBy = createAggregator(function(result, value, key) {
+      (hasOwnProperty.call(result, key) ? result[key]++ : result[key] = 1);
+    });
+
+    /**
+     * Checks if the given callback returns truey value for **all** elements of
+     * a collection. The callback is bound to `thisArg` and invoked with three
+     * arguments; (value, index|key, collection).
+     *
+     * If a property name is provided for `callback` the created "_.pluck" style
+     * callback will return the property value of the given element.
+     *
+     * If an object is provided for `callback` the created "_.where" style callback
+     * will return `true` for elements that have the properties of the given object,
+     * else `false`.
+     *
+     * @static
+     * @memberOf _
+     * @alias all
+     * @category Collections
+     * @param {Array|Object|string} collection The collection to iterate over.
+     * @param {Function|Object|string} [callback=identity] The function called
+     *  per iteration. If a property name or object is provided it will be used
+     *  to create a "_.pluck" or "_.where" style callback, respectively.
+     * @param {*} [thisArg] The `this` binding of `callback`.
+     * @returns {boolean} Returns `true` if all elements passed the callback check,
+     *  else `false`.
+     * @example
+     *
+     * _.every([true, 1, null, 'yes']);
+     * // => false
+     *
+     * var characters = [
+     *   { 'name': 'barney', 'age': 36 },
+     *   { 'name': 'fred',   'age': 40 }
+     * ];
+     *
+     * // using "_.pluck" callback shorthand
+     * _.every(characters, 'age');
+     * // => true
+     *
+     * // using "_.where" callback shorthand
+     * _.every(characters, { 'age': 36 });
+     * // => false
+     */
+    function every(collection, callback, thisArg) {
+      var result = true;
+      callback = lodash.createCallback(callback, thisArg, 3);
+
+      var index = -1,
+          length = collection ? collection.length : 0;
+
+      if (typeof length == 'number') {
+        while (++index < length) {
+          if (!(result = !!callback(collection[index], index, collection))) {
+            break;
+          }
+        }
+      } else {
+        forOwn(collection, function(value, index, collection) {
+          return (result = !!callback(value, index, collection));
+        });
+      }
+      return result;
+    }
+
+    /**
+     * Iterates over elements of a collection, returning an array of all elements
+     * the callback returns truey for. The callback is bound to `thisArg` and
+     * invoked with three arguments; (value, index|key, collection).
+     *
+     * If a property name is provided for `callback` the created "_.pluck" style
+     * callback will return the property value of the given element.
+     *
+     * If an object is provided for `callback` the created "_.where" style callback
+     * will return `true` for elements that have the properties of the given object,
+     * else `false`.
+     *
+     * @static
+     * @memberOf _
+     * @alias select
+     * @category Collections
+     * @param {Array|Object|string} collection The collection to iterate over.
+     * @param {Function|Object|string} [callback=identity] The function called
+     *  per iteration. If a property name or object is provided it will be used
+     *  to create a "_.pluck" or "_.where" style callback, respectively.
+     * @param {*} [thisArg] The `this` binding of `callback`.
+     * @returns {Array} Returns a new array of elements that passed the callback check.
+     * @example
+     *
+     * var evens = _.filter([1, 2, 3, 4, 5, 6], function(num) { return num % 2 == 0; });
+     * // => [2, 4, 6]
+     *
+     * var characters = [
+     *   { 'name': 'barney', 'age': 36, 'blocked': false },
+     *   { 'name': 'fred',   'age': 40, 'blocked': true }
+     * ];
+     *
+     * // using "_.pluck" callback shorthand
+     * _.filter(characters, 'blocked');
+     * // => [{ 'name': 'fred', 'age': 40, 'blocked': true }]
+     *
+     * // using "_.where" callback shorthand
+     * _.filter(characters, { 'age': 36 });
+     * // => [{ 'name': 'barney', 'age': 36, 'blocked': false }]
+     */
+    function filter(collection, callback, thisArg) {
+      var result = [];
+      callback = lodash.createCallback(callback, thisArg, 3);
+
+      var index = -1,
+          length = collection ? collection.length : 0;
+
+      if (typeof length == 'number') {
+        while (++index < length) {
+          var value = collection[index];
+          if (callback(value, index, collection)) {
+            result.push(value);
+          }
+        }
+      } else {
+        forOwn(collection, function(value, index, collection) {
+          if (callback(value, index, collection)) {
+            result.push(value);
+          }
+        });
+      }
+      return result;
+    }
+
+    /**
+     * Iterates over elements of a collection, returning the first element that
+     * the callback returns truey for. The callback is bound to `thisArg` and
+     * invoked with three arguments; (value, index|key, collection).
+     *
+     * If a property name is provided for `callback` the created "_.pluck" style
+     * callback will return the property value of the given element.
+     *
+     * If an object is provided for `callback` the created "_.where" style callback
+     * will return `true` for elements that have the properties of the given object,
+     * else `false`.
+     *
+     * @static
+     * @memberOf _
+     * @alias detect, findWhere
+     * @category Collections
+     * @param {Array|Object|string} collection The collection to iterate over.
+     * @param {Function|Object|string} [callback=identity] The function called
+     *  per iteration. If a property name or object is provided it will be used
+     *  to create a "_.pluck" or "_.where" style callback, respectively.
+     * @param {*} [thisArg] The `this` binding of `callback`.
+     * @returns {*} Returns the found element, else `undefined`.
+     * @example
+     *
+     * var characters = [
+     *   { 'name': 'barney',  'age': 36, 'blocked': false },
+     *   { 'name': 'fred',    'age': 40, 'blocked': true },
+     *   { 'name': 'pebbles', 'age': 1,  'blocked': false }
+     * ];
+     *
+     * _.find(characters, function(chr) {
+     *   return chr.age < 40;
+     * });
+     * // => { 'name': 'barney', 'age': 36, 'blocked': false }
+     *
+     * // using "_.where" callback shorthand
+     * _.find(characters, { 'age': 1 });
+     * // =>  { 'name': 'pebbles', 'age': 1, 'blocked': false }
+     *
+     * // using "_.pluck" callback shorthand
+     * _.find(characters, 'blocked');
+     * // => { 'name': 'fred', 'age': 40, 'blocked': true }
+     */
+    function find(collection, callback, thisArg) {
+      callback = lodash.createCallback(callback, thisArg, 3);
+
+      var index = -1,
+          length = collection ? collection.length : 0;
+
+      if (typeof length == 'number') {
+        while (++index < length) {
+          var value = collection[index];
+          if (callback(value, index, collection)) {
+            return value;
+          }
+        }
+      } else {
+        var result;
+        forOwn(collection, function(value, index, collection) {
+          if (callback(value, index, collection)) {
+            result = value;
+            return false;
+          }
+        });
+        return result;
+      }
+    }
+
+    /**
+     * This method is like `_.find` except that it iterates over elements
+     * of a `collection` from right to left.
+     *
+     * @static
+     * @memberOf _
+     * @category Collections
+     * @param {Array|Object|string} collection The collection to iterate over.
+     * @param {Function|Object|string} [callback=identity] The function called
+     *  per iteration. If a property name or object is provided it will be used
+     *  to create a "_.pluck" or "_.where" style callback, respectively.
+     * @param {*} [thisArg] The `this` binding of `callback`.
+     * @returns {*} Returns the found element, else `undefined`.
+     * @example
+     *
+     * _.findLast([1, 2, 3, 4], function(num) {
+     *   return num % 2 == 1;
+     * });
+     * // => 3
+     */
+    function findLast(collection, callback, thisArg) {
+      var result;
+      callback = lodash.createCallback(callback, thisArg, 3);
+      forEachRight(collection, function(value, index, collection) {
+        if (callback(value, index, collection)) {
+          result = value;
+          return false;
+        }
+      });
+      return result;
+    }
+
+    /**
+     * Iterates over elements of a collection, executing the callback for each
+     * element. The callback is bound to `thisArg` and invoked with three arguments;
+     * (value, index|key, collection). Callbacks may exit iteration early by
+     * explicitly returning `false`.
+     *
+     * Note: As with other "Collections" methods, objects with a `length` property
+     * are iterated like arrays. To avoid this behavior `_.forIn` or `_.forOwn`
+     * may be used for object iteration.
+     *
+     * @static
+     * @memberOf _
+     * @alias each
+     * @category Collections
+     * @param {Array|Object|string} collection The collection to iterate over.
+     * @param {Function} [callback=identity] The function called per iteration.
+     * @param {*} [thisArg] The `this` binding of `callback`.
+     * @returns {Array|Object|string} Returns `collection`.
+     * @example
+     *
+     * _([1, 2, 3]).forEach(function(num) { console.log(num); }).join(',');
+     * // => logs each number and returns '1,2,3'
+     *
+     * _.forEach({ 'one': 1, 'two': 2, 'three': 3 }, function(num) { console.log(num); });
+     * // => logs each number and returns the object (property order is not guaranteed across environments)
+     */
+    function forEach(collection, callback, thisArg) {
+      var index = -1,
+          length = collection ? collection.length : 0;
+
+      callback = callback && typeof thisArg == 'undefined' ? callback : baseCreateCallback(callback, thisArg, 3);
+      if (typeof length == 'number') {
+        while (++index < length) {
+          if (callback(collection[index], index, collection) === false) {
+            break;
+          }
+        }
+      } else {
+        forOwn(collection, callback);
+      }
+      return collection;
+    }
+
+    /**
+     * This method is like `_.forEach` except that it iterates over elements
+     * of a `collection` from right to left.
+     *
+     * @static
+     * @memberOf _
+     * @alias eachRight
+     * @category Collections
+     * @param {Array|Object|string} collection The collection to iterate over.
+     * @param {Function} [callback=identity] The function called per iteration.
+     * @param {*} [thisArg] The `this` binding of `callback`.
+     * @returns {Array|Object|string} Returns `collection`.
+     * @example
+     *
+     * _([1, 2, 3]).forEachRight(function(num) { console.log(num); }).join(',');
+     * // => logs each number from right to left and returns '3,2,1'
+     */
+    function forEachRight(collection, callback, thisArg) {
+      var length = collection ? collection.length : 0;
+      callback = callback && typeof thisArg == 'undefined' ? callback : baseCreateCallback(callback, thisArg, 3);
+      if (typeof length == 'number') {
+        while (length--) {
+          if (callback(collection[length], length, collection) === false) {
+            break;
+          }
+        }
+      } else {
+        var props = keys(collection);
+        length = props.length;
+        forOwn(collection, function(value, key, collection) {
+          key = props ? props[--length] : --length;
+          return callback(collection[key], key, collection);
+        });
+      }
+      return collection;
+    }
+
+    /**
+     * Creates an object composed of keys generated from the results of running
+     * each element of a collection through the callback. The corresponding value
+     * of each key is an array of the elements responsible for generating the key.
+     * The callback is bound to `thisArg` and invoked with three arguments;
+     * (value, index|key, collection).
+     *
+     * If a property name is provided for `callback` the created "_.pluck" style
+     * callback will return the property value of the given element.
+     *
+     * If an object is provided for `callback` the created "_.where" style callback
+     * will return `true` for elements that have the properties of the given object,
+     * else `false`
+     *
+     * @static
+     * @memberOf _
+     * @category Collections
+     * @param {Array|Object|string} collection The collection to iterate over.
+     * @param {Function|Object|string} [callback=identity] The function called
+     *  per iteration. If a property name or object is provided it will be used
+     *  to create a "_.pluck" or "_.where" style callback, respectively.
+     * @param {*} [thisArg] The `this` binding of `callback`.
+     * @returns {Object} Returns the composed aggregate object.
+     * @example
+     *
+     * _.groupBy([4.2, 6.1, 6.4], function(num) { return Math.floor(num); });
+     * // => { '4': [4.2], '6': [6.1, 6.4] }
+     *
+     * _.groupBy([4.2, 6.1, 6.4], function(num) { return this.floor(num); }, Math);
+     * // => { '4': [4.2], '6': [6.1, 6.4] }
+     *
+     * // using "_.pluck" callback shorthand
+     * _.groupBy(['one', 'two', 'three'], 'length');
+     * // => { '3': ['one', 'two'], '5': ['three'] }
+     */
+    var groupBy = createAggregator(function(result, value, key) {
+      (hasOwnProperty.call(result, key) ? result[key] : result[key] = []).push(value);
+    });
+
+    /**
+     * Creates an object composed of keys generated from the results of running
+     * each element of the collection through the given callback. The corresponding
+     * value of each key is the last element responsible for generating the key.
+     * The callback is bound to `thisArg` and invoked with three arguments;
+     * (value, index|key, collection).
+     *
+     * If a property name is provided for `callback` the created "_.pluck" style
+     * callback will return the property value of the given element.
+     *
+     * If an object is provided for `callback` the created "_.where" style callback
+     * will return `true` for elements that have the properties of the given object,
+     * else `false`.
+     *
+     * @static
+     * @memberOf _
+     * @category Collections
+     * @param {Array|Object|string} collection The collection to iterate over.
+     * @param {Function|Object|string} [callback=identity] The function called
+     *  per iteration. If a property name or object is provided it will be used
+     *  to create a "_.pluck" or "_.where" style callback, respectively.
+     * @param {*} [thisArg] The `this` binding of `callback`.
+     * @returns {Object} Returns the composed aggregate object.
+     * @example
+     *
+     * var keys = [
+     *   { 'dir': 'left', 'code': 97 },
+     *   { 'dir': 'right', 'code': 100 }
+     * ];
+     *
+     * _.indexBy(keys, 'dir');
+     * // => { 'left': { 'dir': 'left', 'code': 97 }, 'right': { 'dir': 'right', 'code': 100 } }
+     *
+     * _.indexBy(keys, function(key) { return String.fromCharCode(key.code); });
+     * // => { 'a': { 'dir': 'left', 'code': 97 }, 'd': { 'dir': 'right', 'code': 100 } }
+     *
+     * _.indexBy(characters, function(key) { this.fromCharCode(key.code); }, String);
+     * // => { 'a': { 'dir': 'left', 'code': 97 }, 'd': { 'dir': 'right', 'code': 100 } }
+     */
+    var indexBy = createAggregator(function(result, value, key) {
+      result[key] = value;
+    });
+
+    /**
+     * Invokes the method named by `methodName` on each element in the `collection`
+     * returning an array of the results of each invoked method. Additional arguments
+     * will be provided to each invoked method. If `methodName` is a function it
+     * will be invoked for, and `this` bound to, each element in the `collection`.
+     *
+     * @static
+     * @memberOf _
+     * @category Collections
+     * @param {Array|Object|string} collection The collection to iterate over.
+     * @param {Function|string} methodName The name of the method to invoke or
+     *  the function invoked per iteration.
+     * @param {...*} [arg] Arguments to invoke the method with.
+     * @returns {Array} Returns a new array of the results of each invoked method.
+     * @example
+     *
+     * _.invoke([[5, 1, 7], [3, 2, 1]], 'sort');
+     * // => [[1, 5, 7], [1, 2, 3]]
+     *
+     * _.invoke([123, 456], String.prototype.split, '');
+     * // => [['1', '2', '3'], ['4', '5', '6']]
+     */
+    function invoke(collection, methodName) {
+      var args = slice(arguments, 2),
+          index = -1,
+          isFunc = typeof methodName == 'function',
+          length = collection ? collection.length : 0,
+          result = Array(typeof length == 'number' ? length : 0);
+
+      forEach(collection, function(value) {
+        result[++index] = (isFunc ? methodName : value[methodName]).apply(value, args);
+      });
+      return result;
+    }
+
+    /**
+     * Creates an array of values by running each element in the collection
+     * through the callback. The callback is bound to `thisArg` and invoked with
+     * three arguments; (value, index|key, collection).
+     *
+     * If a property name is provided for `callback` the created "_.pluck" style
+     * callback will return the property value of the given element.
+     *
+     * If an object is provided for `callback` the created "_.where" style callback
+     * will return `true` for elements that have the properties of the given object,
+     * else `false`.
+     *
+     * @static
+     * @memberOf _
+     * @alias collect
+     * @category Collections
+     * @param {Array|Object|string} collection The collection to iterate over.
+     * @param {Function|Object|string} [callback=identity] The function called
+     *  per iteration. If a property name or object is provided it will be used
+     *  to create a "_.pluck" or "_.where" style callback, respectively.
+     * @param {*} [thisArg] The `this` binding of `callback`.
+     * @returns {Array} Returns a new array of the results of each `callback` execution.
+     * @example
+     *
+     * _.map([1, 2, 3], function(num) { return num * 3; });
+     * // => [3, 6, 9]
+     *
+     * _.map({ 'one': 1, 'two': 2, 'three': 3 }, function(num) { return num * 3; });
+     * // => [3, 6, 9] (property order is not guaranteed across environments)
+     *
+     * var characters = [
+     *   { 'name': 'barney', 'age': 36 },
+     *   { 'name': 'fred',   'age': 40 }
+     * ];
+     *
+     * // using "_.pluck" callback shorthand
+     * _.map(characters, 'name');
+     * // => ['barney', 'fred']
+     */
+    function map(collection, callback, thisArg) {
+      var index = -1,
+          length = collection ? collection.length : 0;
+
+      callback = lodash.createCallback(callback, thisArg, 3);
+      if (typeof length == 'number') {
+        var result = Array(length);
+        while (++index < length) {
+          result[index] = callback(collection[index], index, collection);
+        }
+      } else {
+        result = [];
+        forOwn(collection, function(value, key, collection) {
+          result[++index] = callback(value, key, collection);
+        });
+      }
+      return result;
+    }
+
+    /**
+     * Retrieves the maximum value of a collection. If the collection is empty or
+     * falsey `-Infinity` is returned. If a callback is provided it will be executed
+     * for each value in the collection to generate the criterion by which the value
+     * is ranked. The callback is bound to `thisArg` and invoked with three
+     * arguments; (value, index, collection).
+     *
+     * If a property name is provided for `callback` the created "_.pluck" style
+     * callback will return the property value of the given element.
+     *
+     * If an object is provided for `callback` the created "_.where" style callback
+     * will return `true` for elements that have the properties of the given object,
+     * else `false`.
+     *
+     * @static
+     * @memberOf _
+     * @category Collections
+     * @param {Array|Object|string} collection The collection to iterate over.
+     * @param {Function|Object|string} [callback=identity] The function called
+     *  per iteration. If a property name or object is provided it will be used
+     *  to create a "_.pluck" or "_.where" style callback, respectively.
+     * @param {*} [thisArg] The `this` binding of `callback`.
+     * @returns {*} Returns the maximum value.
+     * @example
+     *
+     * _.max([4, 2, 8, 6]);
+     * // => 8
+     *
+     * var characters = [
+     *   { 'name': 'barney', 'age': 36 },
+     *   { 'name': 'fred',   'age': 40 }
+     * ];
+     *
+     * _.max(characters, function(chr) { return chr.age; });
+     * // => { 'name': 'fred', 'age': 40 };
+     *
+     * // using "_.pluck" callback shorthand
+     * _.max(characters, 'age');
+     * // => { 'name': 'fred', 'age': 40 };
+     */
+    function max(collection, callback, thisArg) {
+      var computed = -Infinity,
+          result = computed;
+
+      // allows working with functions like `_.map` without using
+      // their `index` argument as a callback
+      if (typeof callback != 'function' && thisArg && thisArg[callback] === collection) {
+        callback = null;
+      }
+      if (callback == null && isArray(collection)) {
+        var index = -1,
+            length = collection.length;
+
+        while (++index < length) {
+          var value = collection[index];
+          if (value > result) {
+            result = value;
+          }
+        }
+      } else {
+        callback = (callback == null && isString(collection))
+          ? charAtCallback
+          : lodash.createCallback(callback, thisArg, 3);
+
+        forEach(collection, function(value, index, collection) {
+          var current = callback(value, index, collection);
+          if (current > computed) {
+            computed = current;
+            result = value;
+          }
+        });
+      }
+      return result;
+    }
+
+    /**
+     * Retrieves the minimum value of a collection. If the collection is empty or
+     * falsey `Infinity` is returned. If a callback is provided it will be executed
+     * for each value in the collection to generate the criterion by which the value
+     * is ranked. The callback is bound to `thisArg` and invoked with three
+     * arguments; (value, index, collection).
+     *
+     * If a property name is provided for `callback` the created "_.pluck" style
+     * callback will return the property value of the given element.
+     *
+     * If an object is provided for `callback` the created "_.where" style callback
+     * will return `true` for elements that have the properties of the given object,
+     * else `false`.
+     *
+     * @static
+     * @memberOf _
+     * @category Collections
+     * @param {Array|Object|string} collection The collection to iterate over.
+     * @param {Function|Object|string} [callback=identity] The function called
+     *  per iteration. If a property name or object is provided it will be used
+     *  to create a "_.pluck" or "_.where" style callback, respectively.
+     * @param {*} [thisArg] The `this` binding of `callback`.
+     * @returns {*} Returns the minimum value.
+     * @example
+     *
+     * _.min([4, 2, 8, 6]);
+     * // => 2
+     *
+     * var characters = [
+     *   { 'name': 'barney', 'age': 36 },
+     *   { 'name': 'fred',   'age': 40 }
+     * ];
+     *
+     * _.min(characters, function(chr) { return chr.age; });
+     * // => { 'name': 'barney', 'age': 36 };
+     *
+     * // using "_.pluck" callback shorthand
+     * _.min(characters, 'age');
+     * // => { 'name': 'barney', 'age': 36 };
+     */
+    function min(collection, callback, thisArg) {
+      var computed = Infinity,
+          result = computed;
+
+      // allows working with functions like `_.map` without using
+      // their `index` argument as a callback
+      if (typeof callback != 'function' && thisArg && thisArg[callback] === collection) {
+        callback = null;
+      }
+      if (callback == null && isArray(collection)) {
+        var index = -1,
+            length = collection.length;
+
+        while (++index < length) {
+          var value = collection[index];
+          if (value < result) {
+            result = value;
+          }
+        }
+      } else {
+        callback = (callback == null && isString(collection))
+          ? charAtCallback
+          : lodash.createCallback(callback, thisArg, 3);
+
+        forEach(collection, function(value, index, collection) {
+          var current = callback(value, index, collection);
+          if (current < computed) {
+            computed = current;
+            result = value;
+          }
+        });
+      }
+      return result;
+    }
+
+    /**
+     * Retrieves the value of a specified property from all elements in the collection.
+     *
+     * @static
+     * @memberOf _
+     * @type Function
+     * @category Collections
+     * @param {Array|Object|string} collection The collection to iterate over.
+     * @param {string} property The name of the property to pluck.
+     * @returns {Array} Returns a new array of property values.
+     * @example
+     *
+     * var characters = [
+     *   { 'name': 'barney', 'age': 36 },
+     *   { 'name': 'fred',   'age': 40 }
+     * ];
+     *
+     * _.pluck(characters, 'name');
+     * // => ['barney', 'fred']
+     */
+    var pluck = map;
+
+    /**
+     * Reduces a collection to a value which is the accumulated result of running
+     * each element in the collection through the callback, where each successive
+     * callback execution consumes the return value of the previous execution. If
+     * `accumulator` is not provided the first element of the collection will be
+     * used as the initial `accumulator` value. The callback is bound to `thisArg`
+     * and invoked with four arguments; (accumulator, value, index|key, collection).
+     *
+     * @static
+     * @memberOf _
+     * @alias foldl, inject
+     * @category Collections
+     * @param {Array|Object|string} collection The collection to iterate over.
+     * @param {Function} [callback=identity] The function called per iteration.
+     * @param {*} [accumulator] Initial value of the accumulator.
+     * @param {*} [thisArg] The `this` binding of `callback`.
+     * @returns {*} Returns the accumulated value.
+     * @example
+     *
+     * var sum = _.reduce([1, 2, 3], function(sum, num) {
+     *   return sum + num;
+     * });
+     * // => 6
+     *
+     * var mapped = _.reduce({ 'a': 1, 'b': 2, 'c': 3 }, function(result, num, key) {
+     *   result[key] = num * 3;
+     *   return result;
+     * }, {});
+     * // => { 'a': 3, 'b': 6, 'c': 9 }
+     */
+    function reduce(collection, callback, accumulator, thisArg) {
+      if (!collection) return accumulator;
+      var noaccum = arguments.length < 3;
+      callback = lodash.createCallback(callback, thisArg, 4);
+
+      var index = -1,
+          length = collection.length;
+
+      if (typeof length == 'number') {
+        if (noaccum) {
+          accumulator = collection[++index];
+        }
+        while (++index < length) {
+          accumulator = callback(accumulator, collection[index], index, collection);
+        }
+      } else {
+        forOwn(collection, function(value, index, collection) {
+          accumulator = noaccum
+            ? (noaccum = false, value)
+            : callback(accumulator, value, index, collection)
+        });
+      }
+      return accumulator;
+    }
+
+    /**
+     * This method is like `_.reduce` except that it iterates over elements
+     * of a `collection` from right to left.
+     *
+     * @static
+     * @memberOf _
+     * @alias foldr
+     * @category Collections
+     * @param {Array|Object|string} collection The collection to iterate over.
+     * @param {Function} [callback=identity] The function called per iteration.
+     * @param {*} [accumulator] Initial value of the accumulator.
+     * @param {*} [thisArg] The `this` binding of `callback`.
+     * @returns {*} Returns the accumulated value.
+     * @example
+     *
+     * var list = [[0, 1], [2, 3], [4, 5]];
+     * var flat = _.reduceRight(list, function(a, b) { return a.concat(b); }, []);
+     * // => [4, 5, 2, 3, 0, 1]
+     */
+    function reduceRight(collection, callback, accumulator, thisArg) {
+      var noaccum = arguments.length < 3;
+      callback = lodash.createCallback(callback, thisArg, 4);
+      forEachRight(collection, function(value, index, collection) {
+        accumulator = noaccum
+          ? (noaccum = false, value)
+          : callback(accumulator, value, index, collection);
+      });
+      return accumulator;
+    }
+
+    /**
+     * The opposite of `_.filter` this method returns the elements of a
+     * collection that the callback does **not** return truey for.
+     *
+     * If a property name is provided for `callback` the created "_.pluck" style
+     * callback will return the property value of the given element.
+     *
+     * If an object is provided for `callback` the created "_.where" style callback
+     * will return `true` for elements that have the properties of the given object,
+     * else `false`.
+     *
+     * @static
+     * @memberOf _
+     * @category Collections
+     * @param {Array|Object|string} collection The collection to iterate over.
+     * @param {Function|Object|string} [callback=identity] The function called
+     *  per iteration. If a property name or object is provided it will be used
+     *  to create a "_.pluck" or "_.where" style callback, respectively.
+     * @param {*} [thisArg] The `this` binding of `callback`.
+     * @returns {Array} Returns a new array of elements that failed the callback check.
+     * @example
+     *
+     * var odds = _.reject([1, 2, 3, 4, 5, 6], function(num) { return num % 2 == 0; });
+     * // => [1, 3, 5]
+     *
+     * var characters = [
+     *   { 'name': 'barney', 'age': 36, 'blocked': false },
+     *   { 'name': 'fred',   'age': 40, 'blocked': true }
+     * ];
+     *
+     * // using "_.pluck" callback shorthand
+     * _.reject(characters, 'blocked');
+     * // => [{ 'name': 'barney', 'age': 36, 'blocked': false }]
+     *
+     * // using "_.where" callback shorthand
+     * _.reject(characters, { 'age': 36 });
+     * // => [{ 'name': 'fred', 'age': 40, 'blocked': true }]
+     */
+    function reject(collection, callback, thisArg) {
+      callback = lodash.createCallback(callback, thisArg, 3);
+      return filter(collection, function(value, index, collection) {
+        return !callback(value, index, collection);
+      });
+    }
+
+    /**
+     * Retrieves a random element or `n` random elements from a collection.
+     *
+     * @static
+     * @memberOf _
+     * @category Collections
+     * @param {Array|Object|string} collection The collection to sample.
+     * @param {number} [n] The number of elements to sample.
+     * @param- {Object} [guard] Allows working with functions like `_.map`
+     *  without using their `index` arguments as `n`.
+     * @returns {Array} Returns the random sample(s) of `collection`.
+     * @example
+     *
+     * _.sample([1, 2, 3, 4]);
+     * // => 2
+     *
+     * _.sample([1, 2, 3, 4], 2);
+     * // => [3, 1]
+     */
+    function sample(collection, n, guard) {
+      if (collection && typeof collection.length != 'number') {
+        collection = values(collection);
+      }
+      if (n == null || guard) {
+        return collection ? collection[baseRandom(0, collection.length - 1)] : undefined;
+      }
+      var result = shuffle(collection);
+      result.length = nativeMin(nativeMax(0, n), result.length);
+      return result;
+    }
+
+    /**
+     * Creates an array of shuffled values, using a version of the Fisher-Yates
+     * shuffle. See http://en.wikipedia.org/wiki/Fisher-Yates_shuffle.
+     *
+     * @static
+     * @memberOf _
+     * @category Collections
+     * @param {Array|Object|string} collection The collection to shuffle.
+     * @returns {Array} Returns a new shuffled collection.
+     * @example
+     *
+     * _.shuffle([1, 2, 3, 4, 5, 6]);
+     * // => [4, 1, 6, 3, 5, 2]
+     */
+    function shuffle(collection) {
+      var index = -1,
+          length = collection ? collection.length : 0,
+          result = Array(typeof length == 'number' ? length : 0);
+
+      forEach(collection, function(value) {
+        var rand = baseRandom(0, ++index);
+        result[index] = result[rand];
+        result[rand] = value;
+      });
+      return result;
+    }
+
+    /**
+     * Gets the size of the `collection` by returning `collection.length` for arrays
+     * and array-like objects or the number of own enumerable properties for objects.
+     *
+     * @static
+     * @memberOf _
+     * @category Collections
+     * @param {Array|Object|string} collection The collection to inspect.
+     * @returns {number} Returns `collection.length` or number of own enumerable properties.
+     * @example
+     *
+     * _.size([1, 2]);
+     * // => 2
+     *
+     * _.size({ 'one': 1, 'two': 2, 'three': 3 });
+     * // => 3
+     *
+     * _.size('pebbles');
+     * // => 7
+     */
+    function size(collection) {
+      var length = collection ? collection.length : 0;
+      return typeof length == 'number' ? length : keys(collection).length;
+    }
+
+    /**
+     * Checks if the callback returns a truey value for **any** element of a
+     * collection. The function returns as soon as it finds a passing value and
+     * does not iterate over the entire collection. The callback is bound to
+     * `thisArg` and invoked with three arguments; (value, index|key, collection).
+     *
+     * If a property name is provided for `callback` the created "_.pluck" style
+     * callback will return the property value of the given element.
+     *
+     * If an object is provided for `callback` the created "_.where" style callback
+     * will return `true` for elements that have the properties of the given object,
+     * else `false`.
+     *
+     * @static
+     * @memberOf _
+     * @alias any
+     * @category Collections
+     * @param {Array|Object|string} collection The collection to iterate over.
+     * @param {Function|Object|string} [callback=identity] The function called
+     *  per iteration. If a property name or object is provided it will be used
+     *  to create a "_.pluck" or "_.where" style callback, respectively.
+     * @param {*} [thisArg] The `this` binding of `callback`.
+     * @returns {boolean} Returns `true` if any element passed the callback check,
+     *  else `false`.
+     * @example
+     *
+     * _.some([null, 0, 'yes', false], Boolean);
+     * // => true
+     *
+     * var characters = [
+     *   { 'name': 'barney', 'age': 36, 'blocked': false },
+     *   { 'name': 'fred',   'age': 40, 'blocked': true }
+     * ];
+     *
+     * // using "_.pluck" callback shorthand
+     * _.some(characters, 'blocked');
+     * // => true
+     *
+     * // using "_.where" callback shorthand
+     * _.some(characters, { 'age': 1 });
+     * // => false
+     */
+    function some(collection, callback, thisArg) {
+      var result;
+      callback = lodash.createCallback(callback, thisArg, 3);
+
+      var index = -1,
+          length = collection ? collection.length : 0;
+
+      if (typeof length == 'number') {
+        while (++index < length) {
+          if ((result = callback(collection[index], index, collection))) {
+            break;
+          }
+        }
+      } else {
+        forOwn(collection, function(value, index, collection) {
+          return !(result = callback(value, index, collection));
+        });
+      }
+      return !!result;
+    }
+
+    /**
+     * Creates an array of elements, sorted in ascending order by the results of
+     * running each element in a collection through the callback. This method
+     * performs a stable sort, that is, it will preserve the original sort order
+     * of equal elements. The callback is bound to `thisArg` and invoked with
+     * three arguments; (value, index|key, collection).
+     *
+     * If a property name is provided for `callback` the created "_.pluck" style
+     * callback will return the property value of the given element.
+     *
+     * If an array of property names is provided for `callback` the collection
+     * will be sorted by each property value.
+     *
+     * If an object is provided for `callback` the created "_.where" style callback
+     * will return `true` for elements that have the properties of the given object,
+     * else `false`.
+     *
+     * @static
+     * @memberOf _
+     * @category Collections
+     * @param {Array|Object|string} collection The collection to iterate over.
+     * @param {Array|Function|Object|string} [callback=identity] The function called
+     *  per iteration. If a property name or object is provided it will be used
+     *  to create a "_.pluck" or "_.where" style callback, respectively.
+     * @param {*} [thisArg] The `this` binding of `callback`.
+     * @returns {Array} Returns a new array of sorted elements.
+     * @example
+     *
+     * _.sortBy([1, 2, 3], function(num) { return Math.sin(num); });
+     * // => [3, 1, 2]
+     *
+     * _.sortBy([1, 2, 3], function(num) { return this.sin(num); }, Math);
+     * // => [3, 1, 2]
+     *
+     * var characters = [
+     *   { 'name': 'barney',  'age': 36 },
+     *   { 'name': 'fred',    'age': 40 },
+     *   { 'name': 'barney',  'age': 26 },
+     *   { 'name': 'fred',    'age': 30 }
+     * ];
+     *
+     * // using "_.pluck" callback shorthand
+     * _.map(_.sortBy(characters, 'age'), _.values);
+     * // => [['barney', 26], ['fred', 30], ['barney', 36], ['fred', 40]]
+     *
+     * // sorting by multiple properties
+     * _.map(_.sortBy(characters, ['name', 'age']), _.values);
+     * // = > [['barney', 26], ['barney', 36], ['fred', 30], ['fred', 40]]
+     */
+    function sortBy(collection, callback, thisArg) {
+      var index = -1,
+          isArr = isArray(callback),
+          length = collection ? collection.length : 0,
+          result = Array(typeof length == 'number' ? length : 0);
+
+      if (!isArr) {
+        callback = lodash.createCallback(callback, thisArg, 3);
+      }
+      forEach(collection, function(value, key, collection) {
+        var object = result[++index] = getObject();
+        if (isArr) {
+          object.criteria = map(callback, function(key) { return value[key]; });
+        } else {
+          (object.criteria = getArray())[0] = callback(value, key, collection);
+        }
+        object.index = index;
+        object.value = value;
+      });
+
+      length = result.length;
+      result.sort(compareAscending);
+      while (length--) {
+        var object = result[length];
+        result[length] = object.value;
+        if (!isArr) {
+          releaseArray(object.criteria);
+        }
+        releaseObject(object);
+      }
+      return result;
+    }
+
+    /**
+     * Converts the `collection` to an array.
+     *
+     * @static
+     * @memberOf _
+     * @category Collections
+     * @param {Array|Object|string} collection The collection to convert.
+     * @returns {Array} Returns the new converted array.
+     * @example
+     *
+     * (function() { return _.toArray(arguments).slice(1); })(1, 2, 3, 4);
+     * // => [2, 3, 4]
+     */
+    function toArray(collection) {
+      if (collection && typeof collection.length == 'number') {
+        return slice(collection);
+      }
+      return values(collection);
+    }
+
+    /**
+     * Performs a deep comparison of each element in a `collection` to the given
+     * `properties` object, returning an array of all elements that have equivalent
+     * property values.
+     *
+     * @static
+     * @memberOf _
+     * @type Function
+     * @category Collections
+     * @param {Array|Object|string} collection The collection to iterate over.
+     * @param {Object} props The object of property values to filter by.
+     * @returns {Array} Returns a new array of elements that have the given properties.
+     * @example
+     *
+     * var characters = [
+     *   { 'name': 'barney', 'age': 36, 'pets': ['hoppy'] },
+     *   { 'name': 'fred',   'age': 40, 'pets': ['baby puss', 'dino'] }
+     * ];
+     *
+     * _.where(characters, { 'age': 36 });
+     * // => [{ 'name': 'barney', 'age': 36, 'pets': ['hoppy'] }]
+     *
+     * _.where(characters, { 'pets': ['dino'] });
+     * // => [{ 'name': 'fred', 'age': 40, 'pets': ['baby puss', 'dino'] }]
+     */
+    var where = filter;
+
+    /*--------------------------------------------------------------------------*/
+
+    /**
+     * Creates an array with all falsey values removed. The values `false`, `null`,
+     * `0`, `""`, `undefined`, and `NaN` are all falsey.
+     *
+     * @static
+     * @memberOf _
+     * @category Arrays
+     * @param {Array} array The array to compact.
+     * @returns {Array} Returns a new array of filtered values.
+     * @example
+     *
+     * _.compact([0, 1, false, 2, '', 3]);
+     * // => [1, 2, 3]
+     */
+    function compact(array) {
+      var index = -1,
+          length = array ? array.length : 0,
+          result = [];
+
+      while (++index < length) {
+        var value = array[index];
+        if (value) {
+          result.push(value);
+        }
+      }
+      return result;
+    }
+
+    /**
+     * Creates an array excluding all values of the provided arrays using strict
+     * equality for comparisons, i.e. `===`.
+     *
+     * @static
+     * @memberOf _
+     * @category Arrays
+     * @param {Array} array The array to process.
+     * @param {...Array} [values] The arrays of values to exclude.
+     * @returns {Array} Returns a new array of filtered values.
+     * @example
+     *
+     * _.difference([1, 2, 3, 4, 5], [5, 2, 10]);
+     * // => [1, 3, 4]
+     */
+    function difference(array) {
+      return baseDifference(array, baseFlatten(arguments, true, true, 1));
+    }
+
+    /**
+     * This method is like `_.find` except that it returns the index of the first
+     * element that passes the callback check, instead of the element itself.
+     *
+     * If a property name is provided for `callback` the created "_.pluck" style
+     * callback will return the property value of the given element.
+     *
+     * If an object is provided for `callback` the created "_.where" style callback
+     * will return `true` for elements that have the properties of the given object,
+     * else `false`.
+     *
+     * @static
+     * @memberOf _
+     * @category Arrays
+     * @param {Array} array The array to search.
+     * @param {Function|Object|string} [callback=identity] The function called
+     *  per iteration. If a property name or object is provided it will be used
+     *  to create a "_.pluck" or "_.where" style callback, respectively.
+     * @param {*} [thisArg] The `this` binding of `callback`.
+     * @returns {number} Returns the index of the found element, else `-1`.
+     * @example
+     *
+     * var characters = [
+     *   { 'name': 'barney',  'age': 36, 'blocked': false },
+     *   { 'name': 'fred',    'age': 40, 'blocked': true },
+     *   { 'name': 'pebbles', 'age': 1,  'blocked': false }
+     * ];
+     *
+     * _.findIndex(characters, function(chr) {
+     *   return chr.age < 20;
+     * });
+     * // => 2
+     *
+     * // using "_.where" callback shorthand
+     * _.findIndex(characters, { 'age': 36 });
+     * // => 0
+     *
+     * // using "_.pluck" callback shorthand
+     * _.findIndex(characters, 'blocked');
+     * // => 1
+     */
+    function findIndex(array, callback, thisArg) {
+      var index = -1,
+          length = array ? array.length : 0;
+
+      callback = lodash.createCallback(callback, thisArg, 3);
+      while (++index < length) {
+        if (callback(array[index], index, array)) {
+          return index;
+        }
+      }
+      return -1;
+    }
+
+    /**
+     * This method is like `_.findIndex` except that it iterates over elements
+     * of a `collection` from right to left.
+     *
+     * If a property name is provided for `callback` the created "_.pluck" style
+     * callback will return the property value of the given element.
+     *
+     * If an object is provided for `callback` the created "_.where" style callback
+     * will return `true` for elements that have the properties of the given object,
+     * else `false`.
+     *
+     * @static
+     * @memberOf _
+     * @category Arrays
+     * @param {Array} array The array to search.
+     * @param {Function|Object|string} [callback=identity] The function called
+     *  per iteration. If a property name or object is provided it will be used
+     *  to create a "_.pluck" or "_.where" style callback, respectively.
+     * @param {*} [thisArg] The `this` binding of `callback`.
+     * @returns {number} Returns the index of the found element, else `-1`.
+     * @example
+     *
+     * var characters = [
+     *   { 'name': 'barney',  'age': 36, 'blocked': true },
+     *   { 'name': 'fred',    'age': 40, 'blocked': false },
+     *   { 'name': 'pebbles', 'age': 1,  'blocked': true }
+     * ];
+     *
+     * _.findLastIndex(characters, function(chr) {
+     *   return chr.age > 30;
+     * });
+     * // => 1
+     *
+     * // using "_.where" callback shorthand
+     * _.findLastIndex(characters, { 'age': 36 });
+     * // => 0
+     *
+     * // using "_.pluck" callback shorthand
+     * _.findLastIndex(characters, 'blocked');
+     * // => 2
+     */
+    function findLastIndex(array, callback, thisArg) {
+      var length = array ? array.length : 0;
+      callback = lodash.createCallback(callback, thisArg, 3);
+      while (length--) {
+        if (callback(array[length], length, array)) {
+          return length;
+        }
+      }
+      return -1;
+    }
+
+    /**
+     * Gets the first element or first `n` elements of an array. If a callback
+     * is provided elements at the beginning of the array are returned as long
+     * as the callback returns truey. The callback is bound to `thisArg` and
+     * invoked with three arguments; (value, index, array).
+     *
+     * If a property name is provided for `callback` the created "_.pluck" style
+     * callback will return the property value of the given element.
+     *
+     * If an object is provided for `callback` the created "_.where" style callback
+     * will return `true` for elements that have the properties of the given object,
+     * else `false`.
+     *
+     * @static
+     * @memberOf _
+     * @alias head, take
+     * @category Arrays
+     * @param {Array} array The array to query.
+     * @param {Function|Object|number|string} [callback] The function called
+     *  per element or the number of elements to return. If a property name or
+     *  object is provided it will be used to create a "_.pluck" or "_.where"
+     *  style callback, respectively.
+     * @param {*} [thisArg] The `this` binding of `callback`.
+     * @returns {*} Returns the first element(s) of `array`.
+     * @example
+     *
+     * _.first([1, 2, 3]);
+     * // => 1
+     *
+     * _.first([1, 2, 3], 2);
+     * // => [1, 2]
+     *
+     * _.first([1, 2, 3], function(num) {
+     *   return num < 3;
+     * });
+     * // => [1, 2]
+     *
+     * var characters = [
+     *   { 'name': 'barney',  'blocked': true,  'employer': 'slate' },
+     *   { 'name': 'fred',    'blocked': false, 'employer': 'slate' },
+     *   { 'name': 'pebbles', 'blocked': true,  'employer': 'na' }
+     * ];
+     *
+     * // using "_.pluck" callback shorthand
+     * _.first(characters, 'blocked');
+     * // => [{ 'name': 'barney', 'blocked': true, 'employer': 'slate' }]
+     *
+     * // using "_.where" callback shorthand
+     * _.pluck(_.first(characters, { 'employer': 'slate' }), 'name');
+     * // => ['barney', 'fred']
+     */
+    function first(array, callback, thisArg) {
+      var n = 0,
+          length = array ? array.length : 0;
+
+      if (typeof callback != 'number' && callback != null) {
+        var index = -1;
+        callback = lodash.createCallback(callback, thisArg, 3);
+        while (++index < length && callback(array[index], index, array)) {
+          n++;
+        }
+      } else {
+        n = callback;
+        if (n == null || thisArg) {
+          return array ? array[0] : undefined;
+        }
+      }
+      return slice(array, 0, nativeMin(nativeMax(0, n), length));
+    }
+
+    /**
+     * Flattens a nested array (the nesting can be to any depth). If `isShallow`
+     * is truey, the array will only be flattened a single level. If a callback
+     * is provided each element of the array is passed through the callback before
+     * flattening. The callback is bound to `thisArg` and invoked with three
+     * arguments; (value, index, array).
+     *
+     * If a property name is provided for `callback` the created "_.pluck" style
+     * callback will return the property value of the given element.
+     *
+     * If an object is provided for `callback` the created "_.where" style callback
+     * will return `true` for elements that have the properties of the given object,
+     * else `false`.
+     *
+     * @static
+     * @memberOf _
+     * @category Arrays
+     * @param {Array} array The array to flatten.
+     * @param {boolean} [isShallow=false] A flag to restrict flattening to a single level.
+     * @param {Function|Object|string} [callback=identity] The function called
+     *  per iteration. If a property name or object is provided it will be used
+     *  to create a "_.pluck" or "_.where" style callback, respectively.
+     * @param {*} [thisArg] The `this` binding of `callback`.
+     * @returns {Array} Returns a new flattened array.
+     * @example
+     *
+     * _.flatten([1, [2], [3, [[4]]]]);
+     * // => [1, 2, 3, 4];
+     *
+     * _.flatten([1, [2], [3, [[4]]]], true);
+     * // => [1, 2, 3, [[4]]];
+     *
+     * var characters = [
+     *   { 'name': 'barney', 'age': 30, 'pets': ['hoppy'] },
+     *   { 'name': 'fred',   'age': 40, 'pets': ['baby puss', 'dino'] }
+     * ];
+     *
+     * // using "_.pluck" callback shorthand
+     * _.flatten(characters, 'pets');
+     * // => ['hoppy', 'baby puss', 'dino']
+     */
+    function flatten(array, isShallow, callback, thisArg) {
+      // juggle arguments
+      if (typeof isShallow != 'boolean' && isShallow != null) {
+        thisArg = callback;
+        callback = (typeof isShallow != 'function' && thisArg && thisArg[isShallow] === array) ? null : isShallow;
+        isShallow = false;
+      }
+      if (callback != null) {
+        array = map(array, callback, thisArg);
+      }
+      return baseFlatten(array, isShallow);
+    }
+
+    /**
+     * Gets the index at which the first occurrence of `value` is found using
+     * strict equality for comparisons, i.e. `===`. If the array is already sorted
+     * providing `true` for `fromIndex` will run a faster binary search.
+     *
+     * @static
+     * @memberOf _
+     * @category Arrays
+     * @param {Array} array The array to search.
+     * @param {*} value The value to search for.
+     * @param {boolean|number} [fromIndex=0] The index to search from or `true`
+     *  to perform a binary search on a sorted array.
+     * @returns {number} Returns the index of the matched value or `-1`.
+     * @example
+     *
+     * _.indexOf([1, 2, 3, 1, 2, 3], 2);
+     * // => 1
+     *
+     * _.indexOf([1, 2, 3, 1, 2, 3], 2, 3);
+     * // => 4
+     *
+     * _.indexOf([1, 1, 2, 2, 3, 3], 2, true);
+     * // => 2
+     */
+    function indexOf(array, value, fromIndex) {
+      if (typeof fromIndex == 'number') {
+        var length = array ? array.length : 0;
+        fromIndex = (fromIndex < 0 ? nativeMax(0, length + fromIndex) : fromIndex || 0);
+      } else if (fromIndex) {
+        var index = sortedIndex(array, value);
+        return array[index] === value ? index : -1;
+      }
+      return baseIndexOf(array, value, fromIndex);
+    }
+
+    /**
+     * Gets all but the last element or last `n` elements of an array. If a
+     * callback is provided elements at the end of the array are excluded from
+     * the result as long as the callback returns truey. The callback is bound
+     * to `thisArg` and invoked with three arguments; (value, index, array).
+     *
+     * If a property name is provided for `callback` the created "_.pluck" style
+     * callback will return the property value of the given element.
+     *
+     * If an object is provided for `callback` the created "_.where" style callback
+     * will return `true` for elements that have the properties of the given object,
+     * else `false`.
+     *
+     * @static
+     * @memberOf _
+     * @category Arrays
+     * @param {Array} array The array to query.
+     * @param {Function|Object|number|string} [callback=1] The function called
+     *  per element or the number of elements to exclude. If a property name or
+     *  object is provided it will be used to create a "_.pluck" or "_.where"
+     *  style callback, respectively.
+     * @param {*} [thisArg] The `this` binding of `callback`.
+     * @returns {Array} Returns a slice of `array`.
+     * @example
+     *
+     * _.initial([1, 2, 3]);
+     * // => [1, 2]
+     *
+     * _.initial([1, 2, 3], 2);
+     * // => [1]
+     *
+     * _.initial([1, 2, 3], function(num) {
+     *   return num > 1;
+     * });
+     * // => [1]
+     *
+     * var characters = [
+     *   { 'name': 'barney',  'blocked': false, 'employer': 'slate' },
+     *   { 'name': 'fred',    'blocked': true,  'employer': 'slate' },
+     *   { 'name': 'pebbles', 'blocked': true,  'employer': 'na' }
+     * ];
+     *
+     * // using "_.pluck" callback shorthand
+     * _.initial(characters, 'blocked');
+     * // => [{ 'name': 'barney',  'blocked': false, 'employer': 'slate' }]
+     *
+     * // using "_.where" callback shorthand
+     * _.pluck(_.initial(characters, { 'employer': 'na' }), 'name');
+     * // => ['barney', 'fred']
+     */
+    function initial(array, callback, thisArg) {
+      var n = 0,
+          length = array ? array.length : 0;
+
+      if (typeof callback != 'number' && callback != null) {
+        var index = length;
+        callback = lodash.createCallback(callback, thisArg, 3);
+        while (index-- && callback(array[index], index, array)) {
+          n++;
+        }
+      } else {
+        n = (callback == null || thisArg) ? 1 : callback || n;
+      }
+      return slice(array, 0, nativeMin(nativeMax(0, length - n), length));
+    }
+
+    /**
+     * Creates an array of unique values present in all provided arrays using
+     * strict equality for comparisons, i.e. `===`.
+     *
+     * @static
+     * @memberOf _
+     * @category Arrays
+     * @param {...Array} [array] The arrays to inspect.
+     * @returns {Array} Returns an array of shared values.
+     * @example
+     *
+     * _.intersection([1, 2, 3], [5, 2, 1, 4], [2, 1]);
+     * // => [1, 2]
+     */
+    function intersection() {
+      var args = [],
+          argsIndex = -1,
+          argsLength = arguments.length,
+          caches = getArray(),
+          indexOf = getIndexOf(),
+          trustIndexOf = indexOf === baseIndexOf,
+          seen = getArray();
+
+      while (++argsIndex < argsLength) {
+        var value = arguments[argsIndex];
+        if (isArray(value) || isArguments(value)) {
+          args.push(value);
+          caches.push(trustIndexOf && value.length >= largeArraySize &&
+            createCache(argsIndex ? args[argsIndex] : seen));
+        }
+      }
+      var array = args[0],
+          index = -1,
+          length = array ? array.length : 0,
+          result = [];
+
+      outer:
+      while (++index < length) {
+        var cache = caches[0];
+        value = array[index];
+
+        if ((cache ? cacheIndexOf(cache, value) : indexOf(seen, value)) < 0) {
+          argsIndex = argsLength;
+          (cache || seen).push(value);
+          while (--argsIndex) {
+            cache = caches[argsIndex];
+            if ((cache ? cacheIndexOf(cache, value) : indexOf(args[argsIndex], value)) < 0) {
+              continue outer;
+            }
+          }
+          result.push(value);
+        }
+      }
+      while (argsLength--) {
+        cache = caches[argsLength];
+        if (cache) {
+          releaseObject(cache);
+        }
+      }
+      releaseArray(caches);
+      releaseArray(seen);
+      return result;
+    }
+
+    /**
+     * Gets the last element or last `n` elements of an array. If a callback is
+     * provided elements at the end of the array are returned as long as the
+     * callback returns truey. The callback is bound to `thisArg` and invoked
+     * with three arguments; (value, index, array).
+     *
+     * If a property name is provided for `callback` the created "_.pluck" style
+     * callback will return the property value of the given element.
+     *
+     * If an object is provided for `callback` the created "_.where" style callback
+     * will return `true` for elements that have the properties of the given object,
+     * else `false`.
+     *
+     * @static
+     * @memberOf _
+     * @category Arrays
+     * @param {Array} array The array to query.
+     * @param {Function|Object|number|string} [callback] The function called
+     *  per element or the number of elements to return. If a property name or
+     *  object is provided it will be used to create a "_.pluck" or "_.where"
+     *  style callback, respectively.
+     * @param {*} [thisArg] The `this` binding of `callback`.
+     * @returns {*} Returns the last element(s) of `array`.
+     * @example
+     *
+     * _.last([1, 2, 3]);
+     * // => 3
+     *
+     * _.last([1, 2, 3], 2);
+     * // => [2, 3]
+     *
+     * _.last([1, 2, 3], function(num) {
+     *   return num > 1;
+     * });
+     * // => [2, 3]
+     *
+     * var characters = [
+     *   { 'name': 'barney',  'blocked': false, 'employer': 'slate' },
+     *   { 'name': 'fred',    'blocked': true,  'employer': 'slate' },
+     *   { 'name': 'pebbles', 'blocked': true,  'employer': 'na' }
+     * ];
+     *
+     * // using "_.pluck" callback shorthand
+     * _.pluck(_.last(characters, 'blocked'), 'name');
+     * // => ['fred', 'pebbles']
+     *
+     * // using "_.where" callback shorthand
+     * _.last(characters, { 'employer': 'na' });
+     * // => [{ 'name': 'pebbles', 'blocked': true, 'employer': 'na' }]
+     */
+    function last(array, callback, thisArg) {
+      var n = 0,
+          length = array ? array.length : 0;
+
+      if (typeof callback != 'number' && callback != null) {
+        var index = length;
+        callback = lodash.createCallback(callback, thisArg, 3);
+        while (index-- && callback(array[index], index, array)) {
+          n++;
+        }
+      } else {
+        n = callback;
+        if (n == null || thisArg) {
+          return array ? array[length - 1] : undefined;
+        }
+      }
+      return slice(array, nativeMax(0, length - n));
+    }
+
+    /**
+     * Gets the index at which the last occurrence of `value` is found using strict
+     * equality for comparisons, i.e. `===`. If `fromIndex` is negative, it is used
+     * as the offset from the end of the collection.
+     *
+     * If a property name is provided for `callback` the created "_.pluck" style
+     * callback will return the property value of the given element.
+     *
+     * If an object is provided for `callback` the created "_.where" style callback
+     * will return `true` for elements that have the properties of the given object,
+     * else `false`.
+     *
+     * @static
+     * @memberOf _
+     * @category Arrays
+     * @param {Array} array The array to search.
+     * @param {*} value The value to search for.
+     * @param {number} [fromIndex=array.length-1] The index to search from.
+     * @returns {number} Returns the index of the matched value or `-1`.
+     * @example
+     *
+     * _.lastIndexOf([1, 2, 3, 1, 2, 3], 2);
+     * // => 4
+     *
+     * _.lastIndexOf([1, 2, 3, 1, 2, 3], 2, 3);
+     * // => 1
+     */
+    function lastIndexOf(array, value, fromIndex) {
+      var index = array ? array.length : 0;
+      if (typeof fromIndex == 'number') {
+        index = (fromIndex < 0 ? nativeMax(0, index + fromIndex) : nativeMin(fromIndex, index - 1)) + 1;
+      }
+      while (index--) {
+        if (array[index] === value) {
+          return index;
+        }
+      }
+      return -1;
+    }
+
+    /**
+     * Removes all provided values from the given array using strict equality for
+     * comparisons, i.e. `===`.
+     *
+     * @static
+     * @memberOf _
+     * @category Arrays
+     * @param {Array} array The array to modify.
+     * @param {...*} [value] The values to remove.
+     * @returns {Array} Returns `array`.
+     * @example
+     *
+     * var array = [1, 2, 3, 1, 2, 3];
+     * _.pull(array, 2, 3);
+     * console.log(array);
+     * // => [1, 1]
+     */
+    function pull(array) {
+      var args = arguments,
+          argsIndex = 0,
+          argsLength = args.length,
+          length = array ? array.length : 0;
+
+      while (++argsIndex < argsLength) {
+        var index = -1,
+            value = args[argsIndex];
+        while (++index < length) {
+          if (array[index] === value) {
+            splice.call(array, index--, 1);
+            length--;
+          }
+        }
+      }
+      return array;
+    }
+
+    /**
+     * Creates an array of numbers (positive and/or negative) progressing from
+     * `start` up to but not including `end`. If `start` is less than `stop` a
+     * zero-length range is created unless a negative `step` is specified.
+     *
+     * @static
+     * @memberOf _
+     * @category Arrays
+     * @param {number} [start=0] The start of the range.
+     * @param {number} end The end of the range.
+     * @param {number} [step=1] The value to increment or decrement by.
+     * @returns {Array} Returns a new range array.
+     * @example
+     *
+     * _.range(4);
+     * // => [0, 1, 2, 3]
+     *
+     * _.range(1, 5);
+     * // => [1, 2, 3, 4]
+     *
+     * _.range(0, 20, 5);
+     * // => [0, 5, 10, 15]
+     *
+     * _.range(0, -4, -1);
+     * // => [0, -1, -2, -3]
+     *
+     * _.range(1, 4, 0);
+     * // => [1, 1, 1]
+     *
+     * _.range(0);
+     * // => []
+     */
+    function range(start, end, step) {
+      start = +start || 0;
+      step = typeof step == 'number' ? step : (+step || 1);
+
+      if (end == null) {
+        end = start;
+        start = 0;
+      }
+      // use `Array(length)` so engines like Chakra and V8 avoid slower modes
+      // http://youtu.be/XAqIpGU8ZZk#t=17m25s
+      var index = -1,
+          length = nativeMax(0, ceil((end - start) / (step || 1))),
+          result = Array(length);
+
+      while (++index < length) {
+        result[index] = start;
+        start += step;
+      }
+      return result;
+    }
+
+    /**
+     * Removes all elements from an array that the callback returns truey for
+     * and returns an array of removed elements. The callback is bound to `thisArg`
+     * and invoked with three arguments; (value, index, array).
+     *
+     * If a property name is provided for `callback` the created "_.pluck" style
+     * callback will return the property value of the given element.
+     *
+     * If an object is provided for `callback` the created "_.where" style callback
+     * will return `true` for elements that have the properties of the given object,
+     * else `false`.
+     *
+     * @static
+     * @memberOf _
+     * @category Arrays
+     * @param {Array} array The array to modify.
+     * @param {Function|Object|string} [callback=identity] The function called
+     *  per iteration. If a property name or object is provided it will be used
+     *  to create a "_.pluck" or "_.where" style callback, respectively.
+     * @param {*} [thisArg] The `this` binding of `callback`.
+     * @returns {Array} Returns a new array of removed elements.
+     * @example
+     *
+     * var array = [1, 2, 3, 4, 5, 6];
+     * var evens = _.remove(array, function(num) { return num % 2 == 0; });
+     *
+     * console.log(array);
+     * // => [1, 3, 5]
+     *
+     * console.log(evens);
+     * // => [2, 4, 6]
+     */
+    function remove(array, callback, thisArg) {
+      var index = -1,
+          length = array ? array.length : 0,
+          result = [];
+
+      callback = lodash.createCallback(callback, thisArg, 3);
+      while (++index < length) {
+        var value = array[index];
+        if (callback(value, index, array)) {
+          result.push(value);
+          splice.call(array, index--, 1);
+          length--;
+        }
+      }
+      return result;
+    }
+
+    /**
+     * The opposite of `_.initial` this method gets all but the first element or
+     * first `n` elements of an array. If a callback function is provided elements
+     * at the beginning of the array are excluded from the result as long as the
+     * callback returns truey. The callback is bound to `thisArg` and invoked
+     * with three arguments; (value, index, array).
+     *
+     * If a property name is provided for `callback` the created "_.pluck" style
+     * callback will return the property value of the given element.
+     *
+     * If an object is provided for `callback` the created "_.where" style callback
+     * will return `true` for elements that have the properties of the given object,
+     * else `false`.
+     *
+     * @static
+     * @memberOf _
+     * @alias drop, tail
+     * @category Arrays
+     * @param {Array} array The array to query.
+     * @param {Function|Object|number|string} [callback=1] The function called
+     *  per element or the number of elements to exclude. If a property name or
+     *  object is provided it will be used to create a "_.pluck" or "_.where"
+     *  style callback, respectively.
+     * @param {*} [thisArg] The `this` binding of `callback`.
+     * @returns {Array} Returns a slice of `array`.
+     * @example
+     *
+     * _.rest([1, 2, 3]);
+     * // => [2, 3]
+     *
+     * _.rest([1, 2, 3], 2);
+     * // => [3]
+     *
+     * _.rest([1, 2, 3], function(num) {
+     *   return num < 3;
+     * });
+     * // => [3]
+     *
+     * var characters = [
+     *   { 'name': 'barney',  'blocked': true,  'employer': 'slate' },
+     *   { 'name': 'fred',    'blocked': false,  'employer': 'slate' },
+     *   { 'name': 'pebbles', 'blocked': true, 'employer': 'na' }
+     * ];
+     *
+     * // using "_.pluck" callback shorthand
+     * _.pluck(_.rest(characters, 'blocked'), 'name');
+     * // => ['fred', 'pebbles']
+     *
+     * // using "_.where" callback shorthand
+     * _.rest(characters, { 'employer': 'slate' });
+     * // => [{ 'name': 'pebbles', 'blocked': true, 'employer': 'na' }]
+     */
+    function rest(array, callback, thisArg) {
+      if (typeof callback != 'number' && callback != null) {
+        var n = 0,
+            index = -1,
+            length = array ? array.length : 0;
+
+        callback = lodash.createCallback(callback, thisArg, 3);
+        while (++index < length && callback(array[index], index, array)) {
+          n++;
+        }
+      } else {
+        n = (callback == null || thisArg) ? 1 : nativeMax(0, callback);
+      }
+      return slice(array, n);
+    }
+
+    /**
+     * Uses a binary search to determine the smallest index at which a value
+     * should be inserted into a given sorted array in order to maintain the sort
+     * order of the array. If a callback is provided it will be executed for
+     * `value` and each element of `array` to compute their sort ranking. The
+     * callback is bound to `thisArg` and invoked with one argument; (value).
+     *
+     * If a property name is provided for `callback` the created "_.pluck" style
+     * callback will return the property value of the given element.
+     *
+     * If an object is provided for `callback` the created "_.where" style callback
+     * will return `true` for elements that have the properties of the given object,
+     * else `false`.
+     *
+     * @static
+     * @memberOf _
+     * @category Arrays
+     * @param {Array} array The array to inspect.
+     * @param {*} value The value to evaluate.
+     * @param {Function|Object|string} [callback=identity] The function called
+     *  per iteration. If a property name or object is provided it will be used
+     *  to create a "_.pluck" or "_.where" style callback, respectively.
+     * @param {*} [thisArg] The `this` binding of `callback`.
+     * @returns {number} Returns the index at which `value` should be inserted
+     *  into `array`.
+     * @example
+     *
+     * _.sortedIndex([20, 30, 50], 40);
+     * // => 2
+     *
+     * // using "_.pluck" callback shorthand
+     * _.sortedIndex([{ 'x': 20 }, { 'x': 30 }, { 'x': 50 }], { 'x': 40 }, 'x');
+     * // => 2
+     *
+     * var dict = {
+     *   'wordToNumber': { 'twenty': 20, 'thirty': 30, 'fourty': 40, 'fifty': 50 }
+     * };
+     *
+     * _.sortedIndex(['twenty', 'thirty', 'fifty'], 'fourty', function(word) {
+     *   return dict.wordToNumber[word];
+     * });
+     * // => 2
+     *
+     * _.sortedIndex(['twenty', 'thirty', 'fifty'], 'fourty', function(word) {
+     *   return this.wordToNumber[word];
+     * }, dict);
+     * // => 2
+     */
+    function sortedIndex(array, value, callback, thisArg) {
+      var low = 0,
+          high = array ? array.length : low;
+
+      // explicitly reference `identity` for better inlining in Firefox
+      callback = callback ? lodash.createCallback(callback, thisArg, 1) : identity;
+      value = callback(value);
+
+      while (low < high) {
+        var mid = (low + high) >>> 1;
+        (callback(array[mid]) < value)
+          ? low = mid + 1
+          : high = mid;
+      }
+      return low;
+    }
+
+    /**
+     * Creates an array of unique values, in order, of the provided arrays using
+     * strict equality for comparisons, i.e. `===`.
+     *
+     * @static
+     * @memberOf _
+     * @category Arrays
+     * @param {...Array} [array] The arrays to inspect.
+     * @returns {Array} Returns an array of combined values.
+     * @example
+     *
+     * _.union([1, 2, 3], [5, 2, 1, 4], [2, 1]);
+     * // => [1, 2, 3, 5, 4]
+     */
+    function union() {
+      return baseUniq(baseFlatten(arguments, true, true));
+    }
+
+    /**
+     * Creates a duplicate-value-free version of an array using strict equality
+     * for comparisons, i.e. `===`. If the array is sorted, providing
+     * `true` for `isSorted` will use a faster algorithm. If a callback is provided
+     * each element of `array` is passed through the callback before uniqueness
+     * is computed. The callback is bound to `thisArg` and invoked with three
+     * arguments; (value, index, array).
+     *
+     * If a property name is provided for `callback` the created "_.pluck" style
+     * callback will return the property value of the given element.
+     *
+     * If an object is provided for `callback` the created "_.where" style callback
+     * will return `true` for elements that have the properties of the given object,
+     * else `false`.
+     *
+     * @static
+     * @memberOf _
+     * @alias unique
+     * @category Arrays
+     * @param {Array} array The array to process.
+     * @param {boolean} [isSorted=false] A flag to indicate that `array` is sorted.
+     * @param {Function|Object|string} [callback=identity] The function called
+     *  per iteration. If a property name or object is provided it will be used
+     *  to create a "_.pluck" or "_.where" style callback, respectively.
+     * @param {*} [thisArg] The `this` binding of `callback`.
+     * @returns {Array} Returns a duplicate-value-free array.
+     * @example
+     *
+     * _.uniq([1, 2, 1, 3, 1]);
+     * // => [1, 2, 3]
+     *
+     * _.uniq([1, 1, 2, 2, 3], true);
+     * // => [1, 2, 3]
+     *
+     * _.uniq(['A', 'b', 'C', 'a', 'B', 'c'], function(letter) { return letter.toLowerCase(); });
+     * // => ['A', 'b', 'C']
+     *
+     * _.uniq([1, 2.5, 3, 1.5, 2, 3.5], function(num) { return this.floor(num); }, Math);
+     * // => [1, 2.5, 3]
+     *
+     * // using "_.pluck" callback shorthand
+     * _.uniq([{ 'x': 1 }, { 'x': 2 }, { 'x': 1 }], 'x');
+     * // => [{ 'x': 1 }, { 'x': 2 }]
+     */
+    function uniq(array, isSorted, callback, thisArg) {
+      // juggle arguments
+      if (typeof isSorted != 'boolean' && isSorted != null) {
+        thisArg = callback;
+        callback = (typeof isSorted != 'function' && thisArg && thisArg[isSorted] === array) ? null : isSorted;
+        isSorted = false;
+      }
+      if (callback != null) {
+        callback = lodash.createCallback(callback, thisArg, 3);
+      }
+      return baseUniq(array, isSorted, callback);
+    }
+
+    /**
+     * Creates an array excluding all provided values using strict equality for
+     * comparisons, i.e. `===`.
+     *
+     * @static
+     * @memberOf _
+     * @category Arrays
+     * @param {Array} array The array to filter.
+     * @param {...*} [value] The values to exclude.
+     * @returns {Array} Returns a new array of filtered values.
+     * @example
+     *
+     * _.without([1, 2, 1, 0, 3, 1, 4], 0, 1);
+     * // => [2, 3, 4]
+     */
+    function without(array) {
+      return baseDifference(array, slice(arguments, 1));
+    }
+
+    /**
+     * Creates an array that is the symmetric difference of the provided arrays.
+     * See http://en.wikipedia.org/wiki/Symmetric_difference.
+     *
+     * @static
+     * @memberOf _
+     * @category Arrays
+     * @param {...Array} [array] The arrays to inspect.
+     * @returns {Array} Returns an array of values.
+     * @example
+     *
+     * _.xor([1, 2, 3], [5, 2, 1, 4]);
+     * // => [3, 5, 4]
+     *
+     * _.xor([1, 2, 5], [2, 3, 5], [3, 4, 5]);
+     * // => [1, 4, 5]
+     */
+    function xor() {
+      var index = -1,
+          length = arguments.length;
+
+      while (++index < length) {
+        var array = arguments[index];
+        if (isArray(array) || isArguments(array)) {
+          var result = result
+            ? baseUniq(baseDifference(result, array).concat(baseDifference(array, result)))
+            : array;
+        }
+      }
+      return result || [];
+    }
+
+    /**
+     * Creates an array of grouped elements, the first of which contains the first
+     * elements of the given arrays, the second of which contains the second
+     * elements of the given arrays, and so on.
+     *
+     * @static
+     * @memberOf _
+     * @alias unzip
+     * @category Arrays
+     * @param {...Array} [array] Arrays to process.
+     * @returns {Array} Returns a new array of grouped elements.
+     * @example
+     *
+     * _.zip(['fred', 'barney'], [30, 40], [true, false]);
+     * // => [['fred', 30, true], ['barney', 40, false]]
+     */
+    function zip() {
+      var array = arguments.length > 1 ? arguments : arguments[0],
+          index = -1,
+          length = array ? max(pluck(array, 'length')) : 0,
+          result = Array(length < 0 ? 0 : length);
+
+      while (++index < length) {
+        result[index] = pluck(array, index);
+      }
+      return result;
+    }
+
+    /**
+     * Creates an object composed from arrays of `keys` and `values`. Provide
+     * either a single two dimensional array, i.e. `[[key1, value1], [key2, value2]]`
+     * or two arrays, one of `keys` and one of corresponding `values`.
+     *
+     * @static
+     * @memberOf _
+     * @alias object
+     * @category Arrays
+     * @param {Array} keys The array of keys.
+     * @param {Array} [values=[]] The array of values.
+     * @returns {Object} Returns an object composed of the given keys and
+     *  corresponding values.
+     * @example
+     *
+     * _.zipObject(['fred', 'barney'], [30, 40]);
+     * // => { 'fred': 30, 'barney': 40 }
+     */
+    function zipObject(keys, values) {
+      var index = -1,
+          length = keys ? keys.length : 0,
+          result = {};
+
+      if (!values && length && !isArray(keys[0])) {
+        values = [];
+      }
+      while (++index < length) {
+        var key = keys[index];
+        if (values) {
+          result[key] = values[index];
+        } else if (key) {
+          result[key[0]] = key[1];
+        }
+      }
+      return result;
+    }
+
+    /*--------------------------------------------------------------------------*/
+
+    /**
+     * Creates a function that executes `func`, with  the `this` binding and
+     * arguments of the created function, only after being called `n` times.
+     *
+     * @static
+     * @memberOf _
+     * @category Functions
+     * @param {number} n The number of times the function must be called before
+     *  `func` is executed.
+     * @param {Function} func The function to restrict.
+     * @returns {Function} Returns the new restricted function.
+     * @example
+     *
+     * var saves = ['profile', 'settings'];
+     *
+     * var done = _.after(saves.length, function() {
+     *   console.log('Done saving!');
+     * });
+     *
+     * _.forEach(saves, function(type) {
+     *   asyncSave({ 'type': type, 'complete': done });
+     * });
+     * // => logs 'Done saving!', after all saves have completed
+     */
+    function after(n, func) {
+      if (!isFunction(func)) {
+        throw new TypeError;
+      }
+      return function() {
+        if (--n < 1) {
+          return func.apply(this, arguments);
+        }
+      };
+    }
+
+    /**
+     * Creates a function that, when called, invokes `func` with the `this`
+     * binding of `thisArg` and prepends any additional `bind` arguments to those
+     * provided to the bound function.
+     *
+     * @static
+     * @memberOf _
+     * @category Functions
+     * @param {Function} func The function to bind.
+     * @param {*} [thisArg] The `this` binding of `func`.
+     * @param {...*} [arg] Arguments to be partially applied.
+     * @returns {Function} Returns the new bound function.
+     * @example
+     *
+     * var func = function(greeting) {
+     *   return greeting + ' ' + this.name;
+     * };
+     *
+     * func = _.bind(func, { 'name': 'fred' }, 'hi');
+     * func();
+     * // => 'hi fred'
+     */
+    function bind(func, thisArg) {
+      return arguments.length > 2
+        ? createWrapper(func, 17, slice(arguments, 2), null, thisArg)
+        : createWrapper(func, 1, null, null, thisArg);
+    }
+
+    /**
+     * Binds methods of an object to the object itself, overwriting the existing
+     * method. Method names may be specified as individual arguments or as arrays
+     * of method names. If no method names are provided all the function properties
+     * of `object` will be bound.
+     *
+     * @static
+     * @memberOf _
+     * @category Functions
+     * @param {Object} object The object to bind and assign the bound methods to.
+     * @param {...string} [methodName] The object method names to
+     *  bind, specified as individual method names or arrays of method names.
+     * @returns {Object} Returns `object`.
+     * @example
+     *
+     * var view = {
+     *   'label': 'docs',
+     *   'onClick': function() { console.log('clicked ' + this.label); }
+     * };
+     *
+     * _.bindAll(view);
+     * jQuery('#docs').on('click', view.onClick);
+     * // => logs 'clicked docs', when the button is clicked
+     */
+    function bindAll(object) {
+      var funcs = arguments.length > 1 ? baseFlatten(arguments, true, false, 1) : functions(object),
+          index = -1,
+          length = funcs.length;
+
+      while (++index < length) {
+        var key = funcs[index];
+        object[key] = createWrapper(object[key], 1, null, null, object);
+      }
+      return object;
+    }
+
+    /**
+     * Creates a function that, when called, invokes the method at `object[key]`
+     * and prepends any additional `bindKey` arguments to those provided to the bound
+     * function. This method differs from `_.bind` by allowing bound functions to
+     * reference methods that will be redefined or don't yet exist.
+     * See http://michaux.ca/articles/lazy-function-definition-pattern.
+     *
+     * @static
+     * @memberOf _
+     * @category Functions
+     * @param {Object} object The object the method belongs to.
+     * @param {string} key The key of the method.
+     * @param {...*} [arg] Arguments to be partially applied.
+     * @returns {Function} Returns the new bound function.
+     * @example
+     *
+     * var object = {
+     *   'name': 'fred',
+     *   'greet': function(greeting) {
+     *     return greeting + ' ' + this.name;
+     *   }
+     * };
+     *
+     * var func = _.bindKey(object, 'greet', 'hi');
+     * func();
+     * // => 'hi fred'
+     *
+     * object.greet = function(greeting) {
+     *   return greeting + 'ya ' + this.name + '!';
+     * };
+     *
+     * func();
+     * // => 'hiya fred!'
+     */
+    function bindKey(object, key) {
+      return arguments.length > 2
+        ? createWrapper(key, 19, slice(arguments, 2), null, object)
+        : createWrapper(key, 3, null, null, object);
+    }
+
+    /**
+     * Creates a function that is the composition of the provided functions,
+     * where each function consumes the return value of the function that follows.
+     * For example, composing the functions `f()`, `g()`, and `h()` produces `f(g(h()))`.
+     * Each function is executed with the `this` binding of the composed function.
+     *
+     * @static
+     * @memberOf _
+     * @category Functions
+     * @param {...Function} [func] Functions to compose.
+     * @returns {Function} Returns the new composed function.
+     * @example
+     *
+     * var realNameMap = {
+     *   'pebbles': 'penelope'
+     * };
+     *
+     * var format = function(name) {
+     *   name = realNameMap[name.toLowerCase()] || name;
+     *   return name.charAt(0).toUpperCase() + name.slice(1).toLowerCase();
+     * };
+     *
+     * var greet = function(formatted) {
+     *   return 'Hiya ' + formatted + '!';
+     * };
+     *
+     * var welcome = _.compose(greet, format);
+     * welcome('pebbles');
+     * // => 'Hiya Penelope!'
+     */
+    function compose() {
+      var funcs = arguments,
+          length = funcs.length;
+
+      while (length--) {
+        if (!isFunction(funcs[length])) {
+          throw new TypeError;
+        }
+      }
+      return function() {
+        var args = arguments,
+            length = funcs.length;
+
+        while (length--) {
+          args = [funcs[length].apply(this, args)];
+        }
+        return args[0];
+      };
+    }
+
+    /**
+     * Creates a function which accepts one or more arguments of `func` that when
+     * invoked either executes `func` returning its result, if all `func` arguments
+     * have been provided, or returns a function that accepts one or more of the
+     * remaining `func` arguments, and so on. The arity of `func` can be specified
+     * if `func.length` is not sufficient.
+     *
+     * @static
+     * @memberOf _
+     * @category Functions
+     * @param {Function} func The function to curry.
+     * @param {number} [arity=func.length] The arity of `func`.
+     * @returns {Function} Returns the new curried function.
+     * @example
+     *
+     * var curried = _.curry(function(a, b, c) {
+     *   console.log(a + b + c);
+     * });
+     *
+     * curried(1)(2)(3);
+     * // => 6
+     *
+     * curried(1, 2)(3);
+     * // => 6
+     *
+     * curried(1, 2, 3);
+     * // => 6
+     */
+    function curry(func, arity) {
+      arity = typeof arity == 'number' ? arity : (+arity || func.length);
+      return createWrapper(func, 4, null, null, null, arity);
+    }
+
+    /**
+     * Creates a function that will delay the execution of `func` until after
+     * `wait` milliseconds have elapsed since the last time it was invoked.
+     * Provide an options object to indicate that `func` should be invoked on
+     * the leading and/or trailing edge of the `wait` timeout. Subsequent calls
+     * to the debounced function will return the result of the last `func` call.
+     *
+     * Note: If `leading` and `trailing` options are `true` `func` will be called
+     * on the trailing edge of the timeout only if the the debounced function is
+     * invoked more than once during the `wait` timeout.
+     *
+     * @static
+     * @memberOf _
+     * @category Functions
+     * @param {Function} func The function to debounce.
+     * @param {number} wait The number of milliseconds to delay.
+     * @param {Object} [options] The options object.
+     * @param {boolean} [options.leading=false] Specify execution on the leading edge of the timeout.
+     * @param {number} [options.maxWait] The maximum time `func` is allowed to be delayed before it's called.
+     * @param {boolean} [options.trailing=true] Specify execution on the trailing edge of the timeout.
+     * @returns {Function} Returns the new debounced function.
+     * @example
+     *
+     * // avoid costly calculations while the window size is in flux
+     * var lazyLayout = _.debounce(calculateLayout, 150);
+     * jQuery(window).on('resize', lazyLayout);
+     *
+     * // execute `sendMail` when the click event is fired, debouncing subsequent calls
+     * jQuery('#postbox').on('click', _.debounce(sendMail, 300, {
+     *   'leading': true,
+     *   'trailing': false
+     * });
+     *
+     * // ensure `batchLog` is executed once after 1 second of debounced calls
+     * var source = new EventSource('/stream');
+     * source.addEventListener('message', _.debounce(batchLog, 250, {
+     *   'maxWait': 1000
+     * }, false);
+     */
+    function debounce(func, wait, options) {
+      var args,
+          maxTimeoutId,
+          result,
+          stamp,
+          thisArg,
+          timeoutId,
+          trailingCall,
+          lastCalled = 0,
+          maxWait = false,
+          trailing = true;
+
+      if (!isFunction(func)) {
+        throw new TypeError;
+      }
+      wait = nativeMax(0, wait) || 0;
+      if (options === true) {
+        var leading = true;
+        trailing = false;
+      } else if (isObject(options)) {
+        leading = options.leading;
+        maxWait = 'maxWait' in options && (nativeMax(wait, options.maxWait) || 0);
+        trailing = 'trailing' in options ? options.trailing : trailing;
+      }
+      var delayed = function() {
+        var remaining = wait - (now() - stamp);
+        if (remaining <= 0) {
+          if (maxTimeoutId) {
+            clearTimeout(maxTimeoutId);
+          }
+          var isCalled = trailingCall;
+          maxTimeoutId = timeoutId = trailingCall = undefined;
+          if (isCalled) {
+            lastCalled = now();
+            result = func.apply(thisArg, args);
+            if (!timeoutId && !maxTimeoutId) {
+              args = thisArg = null;
+            }
+          }
+        } else {
+          timeoutId = setTimeout(delayed, remaining);
+        }
+      };
+
+      var maxDelayed = function() {
+        if (timeoutId) {
+          clearTimeout(timeoutId);
+        }
+        maxTimeoutId = timeoutId = trailingCall = undefined;
+        if (trailing || (maxWait !== wait)) {
+          lastCalled = now();
+          result = func.apply(thisArg, args);
+          if (!timeoutId && !maxTimeoutId) {
+            args = thisArg = null;
+          }
+        }
+      };
+
+      return function() {
+        args = arguments;
+        stamp = now();
+        thisArg = this;
+        trailingCall = trailing && (timeoutId || !leading);
+
+        if (maxWait === false) {
+          var leadingCall = leading && !timeoutId;
+        } else {
+          if (!maxTimeoutId && !leading) {
+            lastCalled = stamp;
+          }
+          var remaining = maxWait - (stamp - lastCalled),
+              isCalled = remaining <= 0;
+
+          if (isCalled) {
+            if (maxTimeoutId) {
+              maxTimeoutId = clearTimeout(maxTimeoutId);
+            }
+            lastCalled = stamp;
+            result = func.apply(thisArg, args);
+          }
+          else if (!maxTimeoutId) {
+            maxTimeoutId = setTimeout(maxDelayed, remaining);
+          }
+        }
+        if (isCalled && timeoutId) {
+          timeoutId = clearTimeout(timeoutId);
+        }
+        else if (!timeoutId && wait !== maxWait) {
+          timeoutId = setTimeout(delayed, wait);
+        }
+        if (leadingCall) {
+          isCalled = true;
+          result = func.apply(thisArg, args);
+        }
+        if (isCalled && !timeoutId && !maxTimeoutId) {
+          args = thisArg = null;
+        }
+        return result;
+      };
+    }
+
+    /**
+     * Defers executing the `func` function until the current call stack has cleared.
+     * Additional arguments will be provided to `func` when it is invoked.
+     *
+     * @static
+     * @memberOf _
+     * @category Functions
+     * @param {Function} func The function to defer.
+     * @param {...*} [arg] Arguments to invoke the function with.
+     * @returns {number} Returns the timer id.
+     * @example
+     *
+     * _.defer(function(text) { console.log(text); }, 'deferred');
+     * // logs 'deferred' after one or more milliseconds
+     */
+    function defer(func) {
+      if (!isFunction(func)) {
+        throw new TypeError;
+      }
+      var args = slice(arguments, 1);
+      return setTimeout(function() { func.apply(undefined, args); }, 1);
+    }
+
+    /**
+     * Executes the `func` function after `wait` milliseconds. Additional arguments
+     * will be provided to `func` when it is invoked.
+     *
+     * @static
+     * @memberOf _
+     * @category Functions
+     * @param {Function} func The function to delay.
+     * @param {number} wait The number of milliseconds to delay execution.
+     * @param {...*} [arg] Arguments to invoke the function with.
+     * @returns {number} Returns the timer id.
+     * @example
+     *
+     * _.delay(function(text) { console.log(text); }, 1000, 'later');
+     * // => logs 'later' after one second
+     */
+    function delay(func, wait) {
+      if (!isFunction(func)) {
+        throw new TypeError;
+      }
+      var args = slice(arguments, 2);
+      return setTimeout(function() { func.apply(undefined, args); }, wait);
+    }
+
+    /**
+     * Creates a function that memoizes the result of `func`. If `resolver` is
+     * provided it will be used to determine the cache key for storing the result
+     * based on the arguments provided to the memoized function. By default, the
+     * first argument provided to the memoized function is used as the cache key.
+     * The `func` is executed with the `this` binding of the memoized function.
+     * The result cache is exposed as the `cache` property on the memoized function.
+     *
+     * @static
+     * @memberOf _
+     * @category Functions
+     * @param {Function} func The function to have its output memoized.
+     * @param {Function} [resolver] A function used to resolve the cache key.
+     * @returns {Function} Returns the new memoizing function.
+     * @example
+     *
+     * var fibonacci = _.memoize(function(n) {
+     *   return n < 2 ? n : fibonacci(n - 1) + fibonacci(n - 2);
+     * });
+     *
+     * fibonacci(9)
+     * // => 34
+     *
+     * var data = {
+     *   'fred': { 'name': 'fred', 'age': 40 },
+     *   'pebbles': { 'name': 'pebbles', 'age': 1 }
+     * };
+     *
+     * // modifying the result cache
+     * var get = _.memoize(function(name) { return data[name]; }, _.identity);
+     * get('pebbles');
+     * // => { 'name': 'pebbles', 'age': 1 }
+     *
+     * get.cache.pebbles.name = 'penelope';
+     * get('pebbles');
+     * // => { 'name': 'penelope', 'age': 1 }
+     */
+    function memoize(func, resolver) {
+      if (!isFunction(func)) {
+        throw new TypeError;
+      }
+      var memoized = function() {
+        var cache = memoized.cache,
+            key = resolver ? resolver.apply(this, arguments) : keyPrefix + arguments[0];
+
+        return hasOwnProperty.call(cache, key)
+          ? cache[key]
+          : (cache[key] = func.apply(this, arguments));
+      }
+      memoized.cache = {};
+      return memoized;
+    }
+
+    /**
+     * Creates a function that is restricted to execute `func` once. Repeat calls to
+     * the function will return the value of the first call. The `func` is executed
+     * with the `this` binding of the created function.
+     *
+     * @static
+     * @memberOf _
+     * @category Functions
+     * @param {Function} func The function to restrict.
+     * @returns {Function} Returns the new restricted function.
+     * @example
+     *
+     * var initialize = _.once(createApplication);
+     * initialize();
+     * initialize();
+     * // `initialize` executes `createApplication` once
+     */
+    function once(func) {
+      var ran,
+          result;
+
+      if (!isFunction(func)) {
+        throw new TypeError;
+      }
+      return function() {
+        if (ran) {
+          return result;
+        }
+        ran = true;
+        result = func.apply(this, arguments);
+
+        // clear the `func` variable so the function may be garbage collected
+        func = null;
+        return result;
+      };
+    }
+
+    /**
+     * Creates a function that, when called, invokes `func` with any additional
+     * `partial` arguments prepended to those provided to the new function. This
+     * method is similar to `_.bind` except it does **not** alter the `this` binding.
+     *
+     * @static
+     * @memberOf _
+     * @category Functions
+     * @param {Function} func The function to partially apply arguments to.
+     * @param {...*} [arg] Arguments to be partially applied.
+     * @returns {Function} Returns the new partially applied function.
+     * @example
+     *
+     * var greet = function(greeting, name) { return greeting + ' ' + name; };
+     * var hi = _.partial(greet, 'hi');
+     * hi('fred');
+     * // => 'hi fred'
+     */
+    function partial(func) {
+      return createWrapper(func, 16, slice(arguments, 1));
+    }
+
+    /**
+     * This method is like `_.partial` except that `partial` arguments are
+     * appended to those provided to the new function.
+     *
+     * @static
+     * @memberOf _
+     * @category Functions
+     * @param {Function} func The function to partially apply arguments to.
+     * @param {...*} [arg] Arguments to be partially applied.
+     * @returns {Function} Returns the new partially applied function.
+     * @example
+     *
+     * var defaultsDeep = _.partialRight(_.merge, _.defaults);
+     *
+     * var options = {
+     *   'variable': 'data',
+     *   'imports': { 'jq': $ }
+     * };
+     *
+     * defaultsDeep(options, _.templateSettings);
+     *
+     * options.variable
+     * // => 'data'
+     *
+     * options.imports
+     * // => { '_': _, 'jq': $ }
+     */
+    function partialRight(func) {
+      return createWrapper(func, 32, null, slice(arguments, 1));
+    }
+
+    /**
+     * Creates a function that, when executed, will only call the `func` function
+     * at most once per every `wait` milliseconds. Provide an options object to
+     * indicate that `func` should be invoked on the leading and/or trailing edge
+     * of the `wait` timeout. Subsequent calls to the throttled function will
+     * return the result of the last `func` call.
+     *
+     * Note: If `leading` and `trailing` options are `true` `func` will be called
+     * on the trailing edge of the timeout only if the the throttled function is
+     * invoked more than once during the `wait` timeout.
+     *
+     * @static
+     * @memberOf _
+     * @category Functions
+     * @param {Function} func The function to throttle.
+     * @param {number} wait The number of milliseconds to throttle executions to.
+     * @param {Object} [options] The options object.
+     * @param {boolean} [options.leading=true] Specify execution on the leading edge of the timeout.
+     * @param {boolean} [options.trailing=true] Specify execution on the trailing edge of the timeout.
+     * @returns {Function} Returns the new throttled function.
+     * @example
+     *
+     * // avoid excessively updating the position while scrolling
+     * var throttled = _.throttle(updatePosition, 100);
+     * jQuery(window).on('scroll', throttled);
+     *
+     * // execute `renewToken` when the click event is fired, but not more than once every 5 minutes
+     * jQuery('.interactive').on('click', _.throttle(renewToken, 300000, {
+     *   'trailing': false
+     * }));
+     */
+    function throttle(func, wait, options) {
+      var leading = true,
+          trailing = true;
+
+      if (!isFunction(func)) {
+        throw new TypeError;
+      }
+      if (options === false) {
+        leading = false;
+      } else if (isObject(options)) {
+        leading = 'leading' in options ? options.leading : leading;
+        trailing = 'trailing' in options ? options.trailing : trailing;
+      }
+      debounceOptions.leading = leading;
+      debounceOptions.maxWait = wait;
+      debounceOptions.trailing = trailing;
+
+      return debounce(func, wait, debounceOptions);
+    }
+
+    /**
+     * Creates a function that provides `value` to the wrapper function as its
+     * first argument. Additional arguments provided to the function are appended
+     * to those provided to the wrapper function. The wrapper is executed with
+     * the `this` binding of the created function.
+     *
+     * @static
+     * @memberOf _
+     * @category Functions
+     * @param {*} value The value to wrap.
+     * @param {Function} wrapper The wrapper function.
+     * @returns {Function} Returns the new function.
+     * @example
+     *
+     * var p = _.wrap(_.escape, function(func, text) {
+     *   return '<p>' + func(text) + '</p>';
+     * });
+     *
+     * p('Fred, Wilma, & Pebbles');
+     * // => '<p>Fred, Wilma, &amp; Pebbles</p>'
+     */
+    function wrap(value, wrapper) {
+      return createWrapper(wrapper, 16, [value]);
+    }
+
+    /*--------------------------------------------------------------------------*/
+
+    /**
+     * Creates a function that returns `value`.
+     *
+     * @static
+     * @memberOf _
+     * @category Utilities
+     * @param {*} value The value to return from the new function.
+     * @returns {Function} Returns the new function.
+     * @example
+     *
+     * var object = { 'name': 'fred' };
+     * var getter = _.constant(object);
+     * getter() === object;
+     * // => true
+     */
+    function constant(value) {
+      return function() {
+        return value;
+      };
+    }
+
+    /**
+     * Produces a callback bound to an optional `thisArg`. If `func` is a property
+     * name the created callback will return the property value for a given element.
+     * If `func` is an object the created callback will return `true` for elements
+     * that contain the equivalent object properties, otherwise it will return `false`.
+     *
+     * @static
+     * @memberOf _
+     * @category Utilities
+     * @param {*} [func=identity] The value to convert to a callback.
+     * @param {*} [thisArg] The `this` binding of the created callback.
+     * @param {number} [argCount] The number of arguments the callback accepts.
+     * @returns {Function} Returns a callback function.
+     * @example
+     *
+     * var characters = [
+     *   { 'name': 'barney', 'age': 36 },
+     *   { 'name': 'fred',   'age': 40 }
+     * ];
+     *
+     * // wrap to create custom callback shorthands
+     * _.createCallback = _.wrap(_.createCallback, function(func, callback, thisArg) {
+     *   var match = /^(.+?)__([gl]t)(.+)$/.exec(callback);
+     *   return !match ? func(callback, thisArg) : function(object) {
+     *     return match[2] == 'gt' ? object[match[1]] > match[3] : object[match[1]] < match[3];
+     *   };
+     * });
+     *
+     * _.filter(characters, 'age__gt38');
+     * // => [{ 'name': 'fred', 'age': 40 }]
+     */
+    function createCallback(func, thisArg, argCount) {
+      var type = typeof func;
+      if (func == null || type == 'function') {
+        return baseCreateCallback(func, thisArg, argCount);
+      }
+      // handle "_.pluck" style callback shorthands
+      if (type != 'object') {
+        return property(func);
+      }
+      var props = keys(func),
+          key = props[0],
+          a = func[key];
+
+      // handle "_.where" style callback shorthands
+      if (props.length == 1 && a === a && !isObject(a)) {
+        // fast path the common case of providing an object with a single
+        // property containing a primitive value
+        return function(object) {
+          var b = object[key];
+          return a === b && (a !== 0 || (1 / a == 1 / b));
+        };
+      }
+      return function(object) {
+        var length = props.length,
+            result = false;
+
+        while (length--) {
+          if (!(result = baseIsEqual(object[props[length]], func[props[length]], null, true))) {
+            break;
+          }
+        }
+        return result;
+      };
+    }
+
+    /**
+     * Converts the characters `&`, `<`, `>`, `"`, and `'` in `string` to their
+     * corresponding HTML entities.
+     *
+     * @static
+     * @memberOf _
+     * @category Utilities
+     * @param {string} string The string to escape.
+     * @returns {string} Returns the escaped string.
+     * @example
+     *
+     * _.escape('Fred, Wilma, & Pebbles');
+     * // => 'Fred, Wilma, &amp; Pebbles'
+     */
+    function escape(string) {
+      return string == null ? '' : String(string).replace(reUnescapedHtml, escapeHtmlChar);
+    }
+
+    /**
+     * This method returns the first argument provided to it.
+     *
+     * @static
+     * @memberOf _
+     * @category Utilities
+     * @param {*} value Any value.
+     * @returns {*} Returns `value`.
+     * @example
+     *
+     * var object = { 'name': 'fred' };
+     * _.identity(object) === object;
+     * // => true
+     */
+    function identity(value) {
+      return value;
+    }
+
+    /**
+     * Adds function properties of a source object to the destination object.
+     * If `object` is a function methods will be added to its prototype as well.
+     *
+     * @static
+     * @memberOf _
+     * @category Utilities
+     * @param {Function|Object} [object=lodash] object The destination object.
+     * @param {Object} source The object of functions to add.
+     * @param {Object} [options] The options object.
+     * @param {boolean} [options.chain=true] Specify whether the functions added are chainable.
+     * @example
+     *
+     * function capitalize(string) {
+     *   return string.charAt(0).toUpperCase() + string.slice(1).toLowerCase();
+     * }
+     *
+     * _.mixin({ 'capitalize': capitalize });
+     * _.capitalize('fred');
+     * // => 'Fred'
+     *
+     * _('fred').capitalize().value();
+     * // => 'Fred'
+     *
+     * _.mixin({ 'capitalize': capitalize }, { 'chain': false });
+     * _('fred').capitalize();
+     * // => 'Fred'
+     */
+    function mixin(object, source, options) {
+      var chain = true,
+          methodNames = source && functions(source);
+
+      if (!source || (!options && !methodNames.length)) {
+        if (options == null) {
+          options = source;
+        }
+        ctor = lodashWrapper;
+        source = object;
+        object = lodash;
+        methodNames = functions(source);
+      }
+      if (options === false) {
+        chain = false;
+      } else if (isObject(options) && 'chain' in options) {
+        chain = options.chain;
+      }
+      var ctor = object,
+          isFunc = isFunction(ctor);
+
+      forEach(methodNames, function(methodName) {
+        var func = object[methodName] = source[methodName];
+        if (isFunc) {
+          ctor.prototype[methodName] = function() {
+            var chainAll = this.__chain__,
+                value = this.__wrapped__,
+                args = [value];
+
+            push.apply(args, arguments);
+            var result = func.apply(object, args);
+            if (chain || chainAll) {
+              if (value === result && isObject(result)) {
+                return this;
+              }
+              result = new ctor(result);
+              result.__chain__ = chainAll;
+            }
+            return result;
+          };
+        }
+      });
+    }
+
+    /**
+     * Reverts the '_' variable to its previous value and returns a reference to
+     * the `lodash` function.
+     *
+     * @static
+     * @memberOf _
+     * @category Utilities
+     * @returns {Function} Returns the `lodash` function.
+     * @example
+     *
+     * var lodash = _.noConflict();
+     */
+    function noConflict() {
+      context._ = oldDash;
+      return this;
+    }
+
+    /**
+     * A no-operation function.
+     *
+     * @static
+     * @memberOf _
+     * @category Utilities
+     * @example
+     *
+     * var object = { 'name': 'fred' };
+     * _.noop(object) === undefined;
+     * // => true
+     */
+    function noop() {
+      // no operation performed
+    }
+
+    /**
+     * Gets the number of milliseconds that have elapsed since the Unix epoch
+     * (1 January 1970 00:00:00 UTC).
+     *
+     * @static
+     * @memberOf _
+     * @category Utilities
+     * @example
+     *
+     * var stamp = _.now();
+     * _.defer(function() { console.log(_.now() - stamp); });
+     * // => logs the number of milliseconds it took for the deferred function to be called
+     */
+    var now = isNative(now = Date.now) && now || function() {
+      return new Date().getTime();
+    };
+
+    /**
+     * Converts the given value into an integer of the specified radix.
+     * If `radix` is `undefined` or `0` a `radix` of `10` is used unless the
+     * `value` is a hexadecimal, in which case a `radix` of `16` is used.
+     *
+     * Note: This method avoids differences in native ES3 and ES5 `parseInt`
+     * implementations. See http://es5.github.io/#E.
+     *
+     * @static
+     * @memberOf _
+     * @category Utilities
+     * @param {string} value The value to parse.
+     * @param {number} [radix] The radix used to interpret the value to parse.
+     * @returns {number} Returns the new integer value.
+     * @example
+     *
+     * _.parseInt('08');
+     * // => 8
+     */
+    var parseInt = nativeParseInt(whitespace + '08') == 8 ? nativeParseInt : function(value, radix) {
+      // Firefox < 21 and Opera < 15 follow the ES3 specified implementation of `parseInt`
+      return nativeParseInt(isString(value) ? value.replace(reLeadingSpacesAndZeros, '') : value, radix || 0);
+    };
+
+    /**
+     * Creates a "_.pluck" style function, which returns the `key` value of a
+     * given object.
+     *
+     * @static
+     * @memberOf _
+     * @category Utilities
+     * @param {string} key The name of the property to retrieve.
+     * @returns {Function} Returns the new function.
+     * @example
+     *
+     * var characters = [
+     *   { 'name': 'fred',   'age': 40 },
+     *   { 'name': 'barney', 'age': 36 }
+     * ];
+     *
+     * var getName = _.property('name');
+     *
+     * _.map(characters, getName);
+     * // => ['barney', 'fred']
+     *
+     * _.sortBy(characters, getName);
+     * // => [{ 'name': 'barney', 'age': 36 }, { 'name': 'fred',   'age': 40 }]
+     */
+    function property(key) {
+      return function(object) {
+        return object[key];
+      };
+    }
+
+    /**
+     * Produces a random number between `min` and `max` (inclusive). If only one
+     * argument is provided a number between `0` and the given number will be
+     * returned. If `floating` is truey or either `min` or `max` are floats a
+     * floating-point number will be returned instead of an integer.
+     *
+     * @static
+     * @memberOf _
+     * @category Utilities
+     * @param {number} [min=0] The minimum possible value.
+     * @param {number} [max=1] The maximum possible value.
+     * @param {boolean} [floating=false] Specify returning a floating-point number.
+     * @returns {number} Returns a random number.
+     * @example
+     *
+     * _.random(0, 5);
+     * // => an integer between 0 and 5
+     *
+     * _.random(5);
+     * // => also an integer between 0 and 5
+     *
+     * _.random(5, true);
+     * // => a floating-point number between 0 and 5
+     *
+     * _.random(1.2, 5.2);
+     * // => a floating-point number between 1.2 and 5.2
+     */
+    function random(min, max, floating) {
+      var noMin = min == null,
+          noMax = max == null;
+
+      if (floating == null) {
+        if (typeof min == 'boolean' && noMax) {
+          floating = min;
+          min = 1;
+        }
+        else if (!noMax && typeof max == 'boolean') {
+          floating = max;
+          noMax = true;
+        }
+      }
+      if (noMin && noMax) {
+        max = 1;
+      }
+      min = +min || 0;
+      if (noMax) {
+        max = min;
+        min = 0;
+      } else {
+        max = +max || 0;
+      }
+      if (floating || min % 1 || max % 1) {
+        var rand = nativeRandom();
+        return nativeMin(min + (rand * (max - min + parseFloat('1e-' + ((rand +'').length - 1)))), max);
+      }
+      return baseRandom(min, max);
+    }
+
+    /**
+     * Resolves the value of property `key` on `object`. If `key` is a function
+     * it will be invoked with the `this` binding of `object` and its result returned,
+     * else the property value is returned. If `object` is falsey then `undefined`
+     * is returned.
+     *
+     * @static
+     * @memberOf _
+     * @category Utilities
+     * @param {Object} object The object to inspect.
+     * @param {string} key The name of the property to resolve.
+     * @returns {*} Returns the resolved value.
+     * @example
+     *
+     * var object = {
+     *   'cheese': 'crumpets',
+     *   'stuff': function() {
+     *     return 'nonsense';
+     *   }
+     * };
+     *
+     * _.result(object, 'cheese');
+     * // => 'crumpets'
+     *
+     * _.result(object, 'stuff');
+     * // => 'nonsense'
+     */
+    function result(object, key) {
+      if (object) {
+        var value = object[key];
+        return isFunction(value) ? object[key]() : value;
+      }
+    }
+
+    /**
+     * A micro-templating method that handles arbitrary delimiters, preserves
+     * whitespace, and correctly escapes quotes within interpolated code.
+     *
+     * Note: In the development build, `_.template` utilizes sourceURLs for easier
+     * debugging. See http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/#toc-sourceurl
+     *
+     * For more information on precompiling templates see:
+     * http://lodash.com/custom-builds
+     *
+     * For more information on Chrome extension sandboxes see:
+     * http://developer.chrome.com/stable/extensions/sandboxingEval.html
+     *
+     * @static
+     * @memberOf _
+     * @category Utilities
+     * @param {string} text The template text.
+     * @param {Object} data The data object used to populate the text.
+     * @param {Object} [options] The options object.
+     * @param {RegExp} [options.escape] The "escape" delimiter.
+     * @param {RegExp} [options.evaluate] The "evaluate" delimiter.
+     * @param {Object} [options.imports] An object to import into the template as local variables.
+     * @param {RegExp} [options.interpolate] The "interpolate" delimiter.
+     * @param {string} [sourceURL] The sourceURL of the template's compiled source.
+     * @param {string} [variable] The data object variable name.
+     * @returns {Function|string} Returns a compiled function when no `data` object
+     *  is given, else it returns the interpolated text.
+     * @example
+     *
+     * // using the "interpolate" delimiter to create a compiled template
+     * var compiled = _.template('hello <%= name %>');
+     * compiled({ 'name': 'fred' });
+     * // => 'hello fred'
+     *
+     * // using the "escape" delimiter to escape HTML in data property values
+     * _.template('<b><%- value %></b>', { 'value': '<script>' });
+     * // => '<b>&lt;script&gt;</b>'
+     *
+     * // using the "evaluate" delimiter to generate HTML
+     * var list = '<% _.forEach(people, function(name) { %><li><%- name %></li><% }); %>';
+     * _.template(list, { 'people': ['fred', 'barney'] });
+     * // => '<li>fred</li><li>barney</li>'
+     *
+     * // using the ES6 delimiter as an alternative to the default "interpolate" delimiter
+     * _.template('hello ${ name }', { 'name': 'pebbles' });
+     * // => 'hello pebbles'
+     *
+     * // using the internal `print` function in "evaluate" delimiters
+     * _.template('<% print("hello " + name); %>!', { 'name': 'barney' });
+     * // => 'hello barney!'
+     *
+     * // using a custom template delimiters
+     * _.templateSettings = {
+     *   'interpolate': /{{([\s\S]+?)}}/g
+     * };
+     *
+     * _.template('hello {{ name }}!', { 'name': 'mustache' });
+     * // => 'hello mustache!'
+     *
+     * // using the `imports` option to import jQuery
+     * var list = '<% jq.each(people, function(name) { %><li><%- name %></li><% }); %>';
+     * _.template(list, { 'people': ['fred', 'barney'] }, { 'imports': { 'jq': jQuery } });
+     * // => '<li>fred</li><li>barney</li>'
+     *
+     * // using the `sourceURL` option to specify a custom sourceURL for the template
+     * var compiled = _.template('hello <%= name %>', null, { 'sourceURL': '/basic/greeting.jst' });
+     * compiled(data);
+     * // => find the source of "greeting.jst" under the Sources tab or Resources panel of the web inspector
+     *
+     * // using the `variable` option to ensure a with-statement isn't used in the compiled template
+     * var compiled = _.template('hi <%= data.name %>!', null, { 'variable': 'data' });
+     * compiled.source;
+     * // => function(data) {
+     *   var __t, __p = '', __e = _.escape;
+     *   __p += 'hi ' + ((__t = ( data.name )) == null ? '' : __t) + '!';
+     *   return __p;
+     * }
+     *
+     * // using the `source` property to inline compiled templates for meaningful
+     * // line numbers in error messages and a stack trace
+     * fs.writeFileSync(path.join(cwd, 'jst.js'), '\
+     *   var JST = {\
+     *     "main": ' + _.template(mainText).source + '\
+     *   };\
+     * ');
+     */
+    function template(text, data, options) {
+      // based on John Resig's `tmpl` implementation
+      // http://ejohn.org/blog/javascript-micro-templating/
+      // and Laura Doktorova's doT.js
+      // https://github.com/olado/doT
+      var settings = lodash.templateSettings;
+      text = String(text || '');
+
+      // avoid missing dependencies when `iteratorTemplate` is not defined
+      options = defaults({}, options, settings);
+
+      var imports = defaults({}, options.imports, settings.imports),
+          importsKeys = keys(imports),
+          importsValues = values(imports);
+
+      var isEvaluating,
+          index = 0,
+          interpolate = options.interpolate || reNoMatch,
+          source = "__p += '";
+
+      // compile the regexp to match each delimiter
+      var reDelimiters = RegExp(
+        (options.escape || reNoMatch).source + '|' +
+        interpolate.source + '|' +
+        (interpolate === reInterpolate ? reEsTemplate : reNoMatch).source + '|' +
+        (options.evaluate || reNoMatch).source + '|$'
+      , 'g');
+
+      text.replace(reDelimiters, function(match, escapeValue, interpolateValue, esTemplateValue, evaluateValue, offset) {
+        interpolateValue || (interpolateValue = esTemplateValue);
+
+        // escape characters that cannot be included in string literals
+        source += text.slice(index, offset).replace(reUnescapedString, escapeStringChar);
+
+        // replace delimiters with snippets
+        if (escapeValue) {
+          source += "' +\n__e(" + escapeValue + ") +\n'";
+        }
+        if (evaluateValue) {
+          isEvaluating = true;
+          source += "';\n" + evaluateValue + ";\n__p += '";
+        }
+        if (interpolateValue) {
+          source += "' +\n((__t = (" + interpolateValue + ")) == null ? '' : __t) +\n'";
+        }
+        index = offset + match.length;
+
+        // the JS engine embedded in Adobe products requires returning the `match`
+        // string in order to produce the correct `offset` value
+        return match;
+      });
+
+      source += "';\n";
+
+      // if `variable` is not specified, wrap a with-statement around the generated
+      // code to add the data object to the top of the scope chain
+      var variable = options.variable,
+          hasVariable = variable;
+
+      if (!hasVariable) {
+        variable = 'obj';
+        source = 'with (' + variable + ') {\n' + source + '\n}\n';
+      }
+      // cleanup code by stripping empty strings
+      source = (isEvaluating ? source.replace(reEmptyStringLeading, '') : source)
+        .replace(reEmptyStringMiddle, '$1')
+        .replace(reEmptyStringTrailing, '$1;');
+
+      // frame code as the function body
+      source = 'function(' + variable + ') {\n' +
+        (hasVariable ? '' : variable + ' || (' + variable + ' = {});\n') +
+        "var __t, __p = '', __e = _.escape" +
+        (isEvaluating
+          ? ', __j = Array.prototype.join;\n' +
+            "function print() { __p += __j.call(arguments, '') }\n"
+          : ';\n'
+        ) +
+        source +
+        'return __p\n}';
+
+      // Use a sourceURL for easier debugging.
+      // http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/#toc-sourceurl
+      var sourceURL = '\n/*\n//# sourceURL=' + (options.sourceURL || '/lodash/template/source[' + (templateCounter++) + ']') + '\n*/';
+
+      try {
+        var result = Function(importsKeys, 'return ' + source + sourceURL).apply(undefined, importsValues);
+      } catch(e) {
+        e.source = source;
+        throw e;
+      }
+      if (data) {
+        return result(data);
+      }
+      // provide the compiled function's source by its `toString` method, in
+      // supported environments, or the `source` property as a convenience for
+      // inlining compiled templates during the build process
+      result.source = source;
+      return result;
+    }
+
+    /**
+     * Executes the callback `n` times, returning an array of the results
+     * of each callback execution. The callback is bound to `thisArg` and invoked
+     * with one argument; (index).
+     *
+     * @static
+     * @memberOf _
+     * @category Utilities
+     * @param {number} n The number of times to execute the callback.
+     * @param {Function} callback The function called per iteration.
+     * @param {*} [thisArg] The `this` binding of `callback`.
+     * @returns {Array} Returns an array of the results of each `callback` execution.
+     * @example
+     *
+     * var diceRolls = _.times(3, _.partial(_.random, 1, 6));
+     * // => [3, 6, 4]
+     *
+     * _.times(3, function(n) { mage.castSpell(n); });
+     * // => calls `mage.castSpell(n)` three times, passing `n` of `0`, `1`, and `2` respectively
+     *
+     * _.times(3, function(n) { this.cast(n); }, mage);
+     * // => also calls `mage.castSpell(n)` three times
+     */
+    function times(n, callback, thisArg) {
+      n = (n = +n) > -1 ? n : 0;
+      var index = -1,
+          result = Array(n);
+
+      callback = baseCreateCallback(callback, thisArg, 1);
+      while (++index < n) {
+        result[index] = callback(index);
+      }
+      return result;
+    }
+
+    /**
+     * The inverse of `_.escape` this method converts the HTML entities
+     * `&amp;`, `&lt;`, `&gt;`, `&quot;`, and `&#39;` in `string` to their
+     * corresponding characters.
+     *
+     * @static
+     * @memberOf _
+     * @category Utilities
+     * @param {string} string The string to unescape.
+     * @returns {string} Returns the unescaped string.
+     * @example
+     *
+     * _.unescape('Fred, Barney &amp; Pebbles');
+     * // => 'Fred, Barney & Pebbles'
+     */
+    function unescape(string) {
+      return string == null ? '' : String(string).replace(reEscapedHtml, unescapeHtmlChar);
+    }
+
+    /**
+     * Generates a unique ID. If `prefix` is provided the ID will be appended to it.
+     *
+     * @static
+     * @memberOf _
+     * @category Utilities
+     * @param {string} [prefix] The value to prefix the ID with.
+     * @returns {string} Returns the unique ID.
+     * @example
+     *
+     * _.uniqueId('contact_');
+     * // => 'contact_104'
+     *
+     * _.uniqueId();
+     * // => '105'
+     */
+    function uniqueId(prefix) {
+      var id = ++idCounter;
+      return String(prefix == null ? '' : prefix) + id;
+    }
+
+    /*--------------------------------------------------------------------------*/
+
+    /**
+     * Creates a `lodash` object that wraps the given value with explicit
+     * method chaining enabled.
+     *
+     * @static
+     * @memberOf _
+     * @category Chaining
+     * @param {*} value The value to wrap.
+     * @returns {Object} Returns the wrapper object.
+     * @example
+     *
+     * var characters = [
+     *   { 'name': 'barney',  'age': 36 },
+     *   { 'name': 'fred',    'age': 40 },
+     *   { 'name': 'pebbles', 'age': 1 }
+     * ];
+     *
+     * var youngest = _.chain(characters)
+     *     .sortBy('age')
+     *     .map(function(chr) { return chr.name + ' is ' + chr.age; })
+     *     .first()
+     *     .value();
+     * // => 'pebbles is 1'
+     */
+    function chain(value) {
+      value = new lodashWrapper(value);
+      value.__chain__ = true;
+      return value;
+    }
+
+    /**
+     * Invokes `interceptor` with the `value` as the first argument and then
+     * returns `value`. The purpose of this method is to "tap into" a method
+     * chain in order to perform operations on intermediate results within
+     * the chain.
+     *
+     * @static
+     * @memberOf _
+     * @category Chaining
+     * @param {*} value The value to provide to `interceptor`.
+     * @param {Function} interceptor The function to invoke.
+     * @returns {*} Returns `value`.
+     * @example
+     *
+     * _([1, 2, 3, 4])
+     *  .tap(function(array) { array.pop(); })
+     *  .reverse()
+     *  .value();
+     * // => [3, 2, 1]
+     */
+    function tap(value, interceptor) {
+      interceptor(value);
+      return value;
+    }
+
+    /**
+     * Enables explicit method chaining on the wrapper object.
+     *
+     * @name chain
+     * @memberOf _
+     * @category Chaining
+     * @returns {*} Returns the wrapper object.
+     * @example
+     *
+     * var characters = [
+     *   { 'name': 'barney', 'age': 36 },
+     *   { 'name': 'fred',   'age': 40 }
+     * ];
+     *
+     * // without explicit chaining
+     * _(characters).first();
+     * // => { 'name': 'barney', 'age': 36 }
+     *
+     * // with explicit chaining
+     * _(characters).chain()
+     *   .first()
+     *   .pick('age')
+     *   .value();
+     * // => { 'age': 36 }
+     */
+    function wrapperChain() {
+      this.__chain__ = true;
+      return this;
+    }
+
+    /**
+     * Produces the `toString` result of the wrapped value.
+     *
+     * @name toString
+     * @memberOf _
+     * @category Chaining
+     * @returns {string} Returns the string result.
+     * @example
+     *
+     * _([1, 2, 3]).toString();
+     * // => '1,2,3'
+     */
+    function wrapperToString() {
+      return String(this.__wrapped__);
+    }
+
+    /**
+     * Extracts the wrapped value.
+     *
+     * @name valueOf
+     * @memberOf _
+     * @alias value
+     * @category Chaining
+     * @returns {*} Returns the wrapped value.
+     * @example
+     *
+     * _([1, 2, 3]).valueOf();
+     * // => [1, 2, 3]
+     */
+    function wrapperValueOf() {
+      return this.__wrapped__;
+    }
+
+    /*--------------------------------------------------------------------------*/
+
+    // add functions that return wrapped values when chaining
+    lodash.after = after;
+    lodash.assign = assign;
+    lodash.at = at;
+    lodash.bind = bind;
+    lodash.bindAll = bindAll;
+    lodash.bindKey = bindKey;
+    lodash.chain = chain;
+    lodash.compact = compact;
+    lodash.compose = compose;
+    lodash.constant = constant;
+    lodash.countBy = countBy;
+    lodash.create = create;
+    lodash.createCallback = createCallback;
+    lodash.curry = curry;
+    lodash.debounce = debounce;
+    lodash.defaults = defaults;
+    lodash.defer = defer;
+    lodash.delay = delay;
+    lodash.difference = difference;
+    lodash.filter = filter;
+    lodash.flatten = flatten;
+    lodash.forEach = forEach;
+    lodash.forEachRight = forEachRight;
+    lodash.forIn = forIn;
+    lodash.forInRight = forInRight;
+    lodash.forOwn = forOwn;
+    lodash.forOwnRight = forOwnRight;
+    lodash.functions = functions;
+    lodash.groupBy = groupBy;
+    lodash.indexBy = indexBy;
+    lodash.initial = initial;
+    lodash.intersection = intersection;
+    lodash.invert = invert;
+    lodash.invoke = invoke;
+    lodash.keys = keys;
+    lodash.map = map;
+    lodash.mapValues = mapValues;
+    lodash.max = max;
+    lodash.memoize = memoize;
+    lodash.merge = merge;
+    lodash.min = min;
+    lodash.omit = omit;
+    lodash.once = once;
+    lodash.pairs = pairs;
+    lodash.partial = partial;
+    lodash.partialRight = partialRight;
+    lodash.pick = pick;
+    lodash.pluck = pluck;
+    lodash.property = property;
+    lodash.pull = pull;
+    lodash.range = range;
+    lodash.reject = reject;
+    lodash.remove = remove;
+    lodash.rest = rest;
+    lodash.shuffle = shuffle;
+    lodash.sortBy = sortBy;
+    lodash.tap = tap;
+    lodash.throttle = throttle;
+    lodash.times = times;
+    lodash.toArray = toArray;
+    lodash.transform = transform;
+    lodash.union = union;
+    lodash.uniq = uniq;
+    lodash.values = values;
+    lodash.where = where;
+    lodash.without = without;
+    lodash.wrap = wrap;
+    lodash.xor = xor;
+    lodash.zip = zip;
+    lodash.zipObject = zipObject;
+
+    // add aliases
+    lodash.collect = map;
+    lodash.drop = rest;
+    lodash.each = forEach;
+    lodash.eachRight = forEachRight;
+    lodash.extend = assign;
+    lodash.methods = functions;
+    lodash.object = zipObject;
+    lodash.select = filter;
+    lodash.tail = rest;
+    lodash.unique = uniq;
+    lodash.unzip = zip;
+
+    // add functions to `lodash.prototype`
+    mixin(lodash);
+
+    /*--------------------------------------------------------------------------*/
+
+    // add functions that return unwrapped values when chaining
+    lodash.clone = clone;
+    lodash.cloneDeep = cloneDeep;
+    lodash.contains = contains;
+    lodash.escape = escape;
+    lodash.every = every;
+    lodash.find = find;
+    lodash.findIndex = findIndex;
+    lodash.findKey = findKey;
+    lodash.findLast = findLast;
+    lodash.findLastIndex = findLastIndex;
+    lodash.findLastKey = findLastKey;
+    lodash.has = has;
+    lodash.identity = identity;
+    lodash.indexOf = indexOf;
+    lodash.isArguments = isArguments;
+    lodash.isArray = isArray;
+    lodash.isBoolean = isBoolean;
+    lodash.isDate = isDate;
+    lodash.isElement = isElement;
+    lodash.isEmpty = isEmpty;
+    lodash.isEqual = isEqual;
+    lodash.isFinite = isFinite;
+    lodash.isFunction = isFunction;
+    lodash.isNaN = isNaN;
+    lodash.isNull = isNull;
+    lodash.isNumber = isNumber;
+    lodash.isObject = isObject;
+    lodash.isPlainObject = isPlainObject;
+    lodash.isRegExp = isRegExp;
+    lodash.isString = isString;
+    lodash.isUndefined = isUndefined;
+    lodash.lastIndexOf = lastIndexOf;
+    lodash.mixin = mixin;
+    lodash.noConflict = noConflict;
+    lodash.noop = noop;
+    lodash.now = now;
+    lodash.parseInt = parseInt;
+    lodash.random = random;
+    lodash.reduce = reduce;
+    lodash.reduceRight = reduceRight;
+    lodash.result = result;
+    lodash.runInContext = runInContext;
+    lodash.size = size;
+    lodash.some = some;
+    lodash.sortedIndex = sortedIndex;
+    lodash.template = template;
+    lodash.unescape = unescape;
+    lodash.uniqueId = uniqueId;
+
+    // add aliases
+    lodash.all = every;
+    lodash.any = some;
+    lodash.detect = find;
+    lodash.findWhere = find;
+    lodash.foldl = reduce;
+    lodash.foldr = reduceRight;
+    lodash.include = contains;
+    lodash.inject = reduce;
+
+    mixin(function() {
+      var source = {}
+      forOwn(lodash, function(func, methodName) {
+        if (!lodash.prototype[methodName]) {
+          source[methodName] = func;
+        }
+      });
+      return source;
+    }(), false);
+
+    /*--------------------------------------------------------------------------*/
+
+    // add functions capable of returning wrapped and unwrapped values when chaining
+    lodash.first = first;
+    lodash.last = last;
+    lodash.sample = sample;
+
+    // add aliases
+    lodash.take = first;
+    lodash.head = first;
+
+    forOwn(lodash, function(func, methodName) {
+      var callbackable = methodName !== 'sample';
+      if (!lodash.prototype[methodName]) {
+        lodash.prototype[methodName]= function(n, guard) {
+          var chainAll = this.__chain__,
+              result = func(this.__wrapped__, n, guard);
+
+          return !chainAll && (n == null || (guard && !(callbackable && typeof n == 'function')))
+            ? result
+            : new lodashWrapper(result, chainAll);
+        };
+      }
+    });
+
+    /*--------------------------------------------------------------------------*/
+
+    /**
+     * The semantic version number.
+     *
+     * @static
+     * @memberOf _
+     * @type string
+     */
+    lodash.VERSION = '2.4.1';
+
+    // add "Chaining" functions to the wrapper
+    lodash.prototype.chain = wrapperChain;
+    lodash.prototype.toString = wrapperToString;
+    lodash.prototype.value = wrapperValueOf;
+    lodash.prototype.valueOf = wrapperValueOf;
+
+    // add `Array` functions that return unwrapped values
+    forEach(['join', 'pop', 'shift'], function(methodName) {
+      var func = arrayRef[methodName];
+      lodash.prototype[methodName] = function() {
+        var chainAll = this.__chain__,
+            result = func.apply(this.__wrapped__, arguments);
+
+        return chainAll
+          ? new lodashWrapper(result, chainAll)
+          : result;
+      };
+    });
+
+    // add `Array` functions that return the existing wrapped value
+    forEach(['push', 'reverse', 'sort', 'unshift'], function(methodName) {
+      var func = arrayRef[methodName];
+      lodash.prototype[methodName] = function() {
+        func.apply(this.__wrapped__, arguments);
+        return this;
+      };
+    });
+
+    // add `Array` functions that return new wrapped values
+    forEach(['concat', 'slice', 'splice'], function(methodName) {
+      var func = arrayRef[methodName];
+      lodash.prototype[methodName] = function() {
+        return new lodashWrapper(func.apply(this.__wrapped__, arguments), this.__chain__);
+      };
+    });
+
+    return lodash;
+  }
+
+  /*--------------------------------------------------------------------------*/
+
+  // expose Lo-Dash
+  var _ = runInContext();
+
+  // some AMD build optimizers like r.js check for condition patterns like the following:
+  if (typeof define == 'function' && typeof define.amd == 'object' && define.amd) {
+    // Expose Lo-Dash to the global object even when an AMD loader is present in
+    // case Lo-Dash is loaded with a RequireJS shim config.
+    // See http://requirejs.org/docs/api.html#config-shim
+    root._ = _;
+
+    // define as an anonymous module so, through path mapping, it can be
+    // referenced as the "underscore" module
+    define(function() {
+      return _;
+    });
+  }
+  // check for `exports` after `define` in case a build optimizer adds an `exports` object
+  else if (freeExports && freeModule) {
+    // in Node.js or RingoJS
+    if (moduleExports) {
+      (freeModule.exports = _)._ = _;
+    }
+    // in Narwhal or Rhino -require
+    else {
+      freeExports._ = _;
+    }
+  }
+  else {
+    // in a browser or Rhino
+    root._ = _;
+  }
+}.call(this));
diff --git a/packages/context-coloring/benchmark/fixtures/mkdirp-0.5.0.js b/packages/context-coloring/benchmark/fixtures/mkdirp-0.5.0.js
new file mode 100644
index 0000000..a1742b2
--- /dev/null
+++ b/packages/context-coloring/benchmark/fixtures/mkdirp-0.5.0.js
@@ -0,0 +1,97 @@
+var path = require('path');
+var fs = require('fs');
+
+module.exports = mkdirP.mkdirp = mkdirP.mkdirP = mkdirP;
+
+function mkdirP (p, opts, f, made) {
+    if (typeof opts === 'function') {
+        f = opts;
+        opts = {};
+    }
+    else if (!opts || typeof opts !== 'object') {
+        opts = { mode: opts };
+    }
+    
+    var mode = opts.mode;
+    var xfs = opts.fs || fs;
+    
+    if (mode === undefined) {
+        mode = 0777 & (~process.umask());
+    }
+    if (!made) made = null;
+    
+    var cb = f || function () {};
+    p = path.resolve(p);
+    
+    xfs.mkdir(p, mode, function (er) {
+        if (!er) {
+            made = made || p;
+            return cb(null, made);
+        }
+        switch (er.code) {
+            case 'ENOENT':
+                mkdirP(path.dirname(p), opts, function (er, made) {
+                    if (er) cb(er, made);
+                    else mkdirP(p, opts, cb, made);
+                });
+                break;
+
+            // In the case of any other error, just see if there's a dir
+            // there already.  If so, then hooray!  If not, then something
+            // is borked.
+            default:
+                xfs.stat(p, function (er2, stat) {
+                    // if the stat fails, then that's super weird.
+                    // let the original error be the failure reason.
+                    if (er2 || !stat.isDirectory()) cb(er, made)
+                    else cb(null, made);
+                });
+                break;
+        }
+    });
+}
+
+mkdirP.sync = function sync (p, opts, made) {
+    if (!opts || typeof opts !== 'object') {
+        opts = { mode: opts };
+    }
+    
+    var mode = opts.mode;
+    var xfs = opts.fs || fs;
+    
+    if (mode === undefined) {
+        mode = 0777 & (~process.umask());
+    }
+    if (!made) made = null;
+
+    p = path.resolve(p);
+
+    try {
+        xfs.mkdirSync(p, mode);
+        made = made || p;
+    }
+    catch (err0) {
+        switch (err0.code) {
+            case 'ENOENT' :
+                made = sync(path.dirname(p), opts, made);
+                sync(p, opts, made);
+                break;
+
+            // In the case of any other error, just see if there's a dir
+            // there already.  If so, then hooray!  If not, then something
+            // is borked.
+            default:
+                var stat;
+                try {
+                    stat = xfs.statSync(p);
+                }
+                catch (err1) {
+                    throw err0;
+                }
+                if (!stat.isDirectory()) throw err0;
+                break;
+        }
+    }
+
+    return made;
+};
diff --git a/packages/context-coloring/context-coloring-themes.el b/packages/context-coloring/context-coloring-themes.el
new file mode 100644
index 0000000..a964169
--- /dev/null
+++ b/packages/context-coloring/context-coloring-themes.el
@@ -0,0 +1,122 @@
+;;; context-coloring-themes.el --- Color schemes for Context Coloring. -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2014-2015  Free Software Foundation, Inc.
+
+;; This file is part of GNU Emacs.
+
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; An assortment of color schemes for Context Coloring, many of which are based
+;; on existing color themes and custom themes.
+
+;; To use, simply call `context-coloring-load-theme':
+
+;; (require 'context-coloring)
+;; (context-coloring-load-theme 'zenburn)
+
+;;; Code:
+
+(defvar context-coloring-theme-hash-table (make-hash-table :test 'eq)
+  "Mapping of theme names to theme properties.")
+
+(defun context-coloring-define-theme (theme &rest properties)
+  "Define a theme named THEME for coloring scope levels.
+PROPERTIES is a property list specifiying the following details:
+
+`:colors': List of colors that this theme uses."
+  (puthash
+   theme
+   (lambda ()
+     (apply 'context-coloring-set-colors (plist-get properties :colors)))
+   context-coloring-theme-hash-table))
+
+(defun context-coloring-load-theme (theme)
+  "Apply THEME's colors and other properties for context
+coloring."
+  (let ((function (gethash theme context-coloring-theme-hash-table)))
+    (when (null function)
+      (error (format "No such theme `%s'" theme)))
+    (funcall function)))
+
+(context-coloring-define-theme
+ 'monokai
+ :colors '("#F8F8F2"
+           "#66D9EF"
+           "#A1EFE4"
+           "#A6E22E"
+           "#E6DB74"
+           "#FD971F"
+           "#F92672"
+           "#FD5FF0"
+           "#AE81FF"))
+
+(context-coloring-define-theme
+ 'solarized
+ :colors '("#839496"
+           "#268bd2"
+           "#2aa198"
+           "#859900"
+           "#b58900"
+           "#cb4b16"
+           "#dc322f"
+           "#d33682"
+           "#6c71c4"
+           "#69B7F0"
+           "#69CABF"
+           "#B4C342"
+           "#DEB542"
+           "#F2804F"
+           "#FF6E64"
+           "#F771AC"
+           "#9EA0E5"))
+
+(context-coloring-define-theme
+ 'tango
+ :colors '("#2e3436"
+           "#346604"
+           "#204a87"
+           "#5c3566"
+           "#a40000"
+           "#b35000"
+           "#c4a000"
+           "#8ae234"
+           "#8cc4ff"
+           "#ad7fa8"
+           "#ef2929"
+           "#fcaf3e"
+           "#fce94f"))
+
+(context-coloring-define-theme
+ 'zenburn
+ :colors '("#DCDCCC"
+           "#93E0E3"
+           "#BFEBBF"
+           "#F0DFAF"
+           "#DFAF8F"
+           "#CC9393"
+           "#DC8CC3"
+           "#94BFF3"
+           "#9FC59F"
+           "#D0BF8F"
+           "#DCA3A3"))
+
+(provide 'context-coloring-themes)
+
+;; Local Variables:
+;; eval: (when (fboundp 'rainbow-mode) (rainbow-mode 1))
+;; End:
+
+;;; context-coloring-themes.el ends here
diff --git a/packages/context-coloring/context-coloring.el b/packages/context-coloring/context-coloring.el
new file mode 100644
index 0000000..cd5a8b4
--- /dev/null
+++ b/packages/context-coloring/context-coloring.el
@@ -0,0 +1,518 @@
+;;; context-coloring.el --- Syntax highlighting, except not for syntax. -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2014-2015  Free Software Foundation, Inc.
+
+;; Author: Jackson Ray Hamilton <jackson@jacksonrayhamilton.com>
+;; URL: https://github.com/jacksonrayhamilton/context-coloring
+;; Keywords: context coloring syntax highlighting
+;; Version: 3.1.0
+;; Package-Requires: ((emacs "24") (js2-mode "20150126"))
+
+;; This file is part of GNU Emacs.
+
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; Highlights code according to function context.
+
+;; - Code in the global scope is one color.  Code in functions within the global
+;;   scope is a different color, and code within such functions is another
+;;   color, and so on.
+;; - Identifiers retain the color of the scope in which they are declared.
+
+;; Lexical scope information at-a-glance can assist a programmer in
+;; understanding the overall structure of a program.  It can help to curb nasty
+;; bugs like name shadowing.  A rainbow can indicate excessive complexity. State
+;; change within a closure is easily monitored.
+
+;; By default, Context Coloring still highlights comments and strings
+;; syntactically.  It is still easy to differentiate code from non-code, and
+;; strings cannot be confused for variables.
+
+;; To use, add the following to your ~/.emacs:
+
+;; (require 'context-coloring)
+;; (add-hook 'js-mode-hook 'context-coloring-mode)
+
+;;; Code:
+
+(require 'context-coloring-themes)
+(require 'js2-mode)
+
+
+;;; Constants
+
+(defconst context-coloring-path
+  (file-name-directory (or load-file-name buffer-file-name))
+  "This file's directory.")
+
+
+;;; Customizable options
+
+(defcustom context-coloring-delay 0.25
+  "Delay between a buffer update and colorization.
+
+Increase this if your machine is high-performing.  Decrease it if
+it ain't.
+
+Supported modes: `js-mode', `js3-mode'"
+  :group 'context-coloring)
+
+(defcustom context-coloring-comments-and-strings t
+  "If non-nil, also color comments and strings using `font-lock'."
+  :group 'context-coloring)
+
+(defcustom context-coloring-js-block-scopes nil
+  "If non-nil, also color block scopes in the scope hierarchy in JavaScript.
+
+The block-scope-inducing `let' and `const' are introduced in ES6.
+If you are writing ES6 code, enable this; otherwise, don't.
+
+Supported modes: `js2-mode'"
+  :group 'context-coloring)
+
+(defcustom context-coloring-benchmark-colorization nil
+  "If non-nil, track how long colorization takes and print
+messages with the colorization duration."
+  :group 'context-coloring)
+
+
+;;; Local variables
+
+(defvar-local context-coloring-buffer nil
+  "Reference to this buffer (for timers).")
+
+(defvar-local context-coloring-scopifier-process nil
+  "Reference to the single scopifier process that can be
+  running.")
+
+(defvar-local context-coloring-colorize-idle-timer nil
+  "Reference to the currently-running idle timer.")
+
+(defvar-local context-coloring-changed nil
+  "Indication that the buffer has changed recently, which would
+imply that it should be colorized again by
+`context-coloring-colorize-idle-timer' if that timer is being
+used.")
+
+
+;;; Faces
+
+(defun context-coloring-defface (level tty light dark)
+  (let ((face (intern (format "context-coloring-level-%s-face" level)))
+        (doc (format "Context coloring face, level %s." level)))
+    (eval (macroexpand `(defface ,face
+                          '((((type tty)) (:foreground ,tty))
+                            (((background light)) (:foreground ,light))
+                            (((background dark)) (:foreground ,dark)))
+                          ,doc
+                          :group 'context-coloring)))))
+
+(defvar context-coloring-face-count nil
+  "Number of faces available for context coloring.")
+
+(defun context-coloring-defface-default (level)
+  (context-coloring-defface level "white" "#3f3f3f" "#cdcdcd"))
+
+(defun context-coloring-set-colors-default ()
+  (context-coloring-defface 0 "white"   "#000000" "#ffffff")
+  (context-coloring-defface 1 "yellow"  "#007f80" "#ffff80")
+  (context-coloring-defface 2 "green"   "#001580" "#cdfacd")
+  (context-coloring-defface 3 "cyan"    "#550080" "#d8d8ff")
+  (context-coloring-defface 4 "blue"    "#802b00" "#e7c7ff")
+  (context-coloring-defface 5 "magenta" "#6a8000" "#ffcdcd")
+  (context-coloring-defface 6 "red"     "#008000" "#ffe390")
+  (context-coloring-defface-default 7)
+  (setq context-coloring-face-count 8))
+
+(context-coloring-set-colors-default)
+
+
+;;; Face functions
+
+(defsubst context-coloring-face-symbol (level)
+  "Returns a symbol for a face with LEVEL."
+  ;; `concat' is faster than `format' here.
+  (intern-soft (concat "context-coloring-level-"
+                       (number-to-string level)
+                       "-face")))
+
+(defun context-coloring-set-colors (&rest colors)
+  "Set context coloring's levels' coloring to COLORS, where the
+Nth element of COLORS is level N's color."
+  (setq context-coloring-face-count (length colors))
+  (let ((level 0))
+    (dolist (color colors)
+      ;; Ensure there are available faces to contain new colors.
+      (when (not (context-coloring-face-symbol level))
+        (context-coloring-defface-default level))
+      (set-face-foreground (context-coloring-face-symbol level) color)
+      (setq level (+ level 1)))))
+
+(defsubst context-coloring-level-face (level)
+  "Returns the face name for LEVEL."
+  (context-coloring-face-symbol (min level context-coloring-face-count)))
+
+
+;;; Colorization utilities
+
+(defsubst context-coloring-colorize-region (start end level)
+  "Colorizes characters from the 1-indexed START (inclusive) to
+END (exclusive) with the face corresponding to LEVEL."
+  (add-text-properties
+   start
+   end
+   `(face ,(context-coloring-level-face level))))
+
+(defsubst context-coloring-maybe-colorize-comments-and-strings ()
+  "Colorizes the current buffer's comments and strings if
+`context-coloring-comments-and-strings' is non-nil."
+  (when context-coloring-comments-and-strings
+    (save-excursion
+      (font-lock-fontify-syntactically-region (point-min) (point-max)))))
+
+
+;;; js2-mode colorization
+
+(defvar-local context-coloring-js2-scope-level-hash-table nil
+  "Associates `js2-scope' structures and with their scope
+  levels.")
+
+(defsubst context-coloring-js2-scope-level (scope)
+  "Gets the level of SCOPE."
+  (cond ((gethash scope context-coloring-js2-scope-level-hash-table))
+        (t
+         (let ((level 0)
+               (current-scope scope)
+               enclosing-scope)
+           (while (and current-scope
+                       (js2-node-parent current-scope)
+                       (setq enclosing-scope
+                             (js2-node-get-enclosing-scope current-scope)))
+             (when (or context-coloring-js-block-scopes
+                       (let ((type (js2-scope-type current-scope)))
+                         (or (= type js2-SCRIPT)
+                             (= type js2-FUNCTION)
+                             (= type js2-CATCH))))
+               (setq level (+ level 1)))
+             (setq current-scope enclosing-scope))
+           (puthash scope level context-coloring-js2-scope-level-hash-table)))))
+
+(defsubst context-coloring-js2-local-name-node-p (node)
+  "Determines if NODE is a js2-name-node representing a local
+variable."
+  (and (js2-name-node-p node)
+       (let ((parent (js2-node-parent node)))
+         (not (or (and (js2-object-prop-node-p parent)
+                       (eq node (js2-object-prop-node-left parent)))
+                  (and (js2-prop-get-node-p parent)
+                       ;; For nested property lookup, the node on the left is a
+                       ;; `js2-prop-get-node', so this always works.
+                       (eq node (js2-prop-get-node-right parent))))))))
+
+(defsubst context-coloring-js2-colorize-node (node level)
+  "Colors NODE with the color for LEVEL."
+  (let ((start (js2-node-abs-pos node)))
+    (context-coloring-colorize-region
+     start
+     (+ start (js2-node-len node)) ; End
+     level)))
+
+(defun context-coloring-js2-colorize ()
+  "Colorizes the current buffer using the abstract syntax tree
+generated by js2-mode."
+  ;; Reset the hash table; the old one could be obsolete.
+  (setq context-coloring-js2-scope-level-hash-table (make-hash-table :test 'eq))
+  (with-silent-modifications
+    (js2-visit-ast
+     js2-mode-ast
+     (lambda (node end-p)
+       (when (null end-p)
+         (cond
+          ((js2-scope-p node)
+           (context-coloring-js2-colorize-node
+            node
+            (context-coloring-js2-scope-level node)))
+          ((context-coloring-js2-local-name-node-p node)
+           (let* ((enclosing-scope (js2-node-get-enclosing-scope node))
+                  (defining-scope (js2-get-defining-scope
+                                   enclosing-scope
+                                   (js2-name-node-name node))))
+             ;; The tree seems to be walked lexically, so an entire scope will
+             ;; be colored, including its name nodes, before they are
+             ;; reached. Coloring the nodes defined in that scope would be
+             ;; redundant, so don't do it.
+             (when (not (eq defining-scope enclosing-scope))
+               (context-coloring-js2-colorize-node
+                node
+                (context-coloring-js2-scope-level defining-scope))))))
+         ;; The `t' indicates to search children.
+         t)))
+    (context-coloring-maybe-colorize-comments-and-strings)))
+
+
+;;; Shell command scopification / colorization
+
+(defun context-coloring-apply-tokens (tokens)
+  "Processes a vector of TOKENS to apply context-based coloring
+to the current buffer.  Tokens are 3 integers: start, end, level.
+The vector is flat, with a new token occurring after every 3rd
+element."
+  (with-silent-modifications
+    (let ((i 0)
+          (len (length tokens)))
+      (while (< i len)
+        (context-coloring-colorize-region
+         (elt tokens i)
+         (elt tokens (+ i 1))
+         (elt tokens (+ i 2)))
+        (setq i (+ i 3))))
+    (context-coloring-maybe-colorize-comments-and-strings)))
+
+(defun context-coloring-parse-array (input)
+  "Specialized JSON parser for a flat array of numbers."
+  (vconcat (mapcar 'string-to-number (split-string (substring input 1 -1) ","))))
+
+(defun context-coloring-kill-scopifier ()
+  "Kills the currently-running scopifier process for this
+buffer."
+  (when (not (null context-coloring-scopifier-process))
+    (delete-process context-coloring-scopifier-process)
+    (setq context-coloring-scopifier-process nil)))
+
+(defun context-coloring-scopify-shell-command (command &optional callback)
+  "Invokes a scopifier with the current buffer's contents,
+reading the scopifier's response asynchronously and applying a
+parsed list of tokens to `context-coloring-apply-tokens'.
+
+Invokes CALLBACK when complete."
+
+  ;; Prior running tokenization is implicitly obsolete if this function is
+  ;; called.
+  (context-coloring-kill-scopifier)
+
+  ;; Start the process.
+  (setq context-coloring-scopifier-process
+        (start-process-shell-command "scopifier" nil command))
+
+  (let ((output "")
+        (buffer context-coloring-buffer))
+
+    ;; The process may produce output in multiple chunks. This filter
+    ;; accumulates the chunks into a message.
+    (set-process-filter
+     context-coloring-scopifier-process
+     (lambda (_process chunk)
+       (setq output (concat output chunk))))
+
+    ;; When the process's message is complete, this sentinel parses it as JSON
+    ;; and applies the tokens to the buffer.
+    (set-process-sentinel
+     context-coloring-scopifier-process
+     (lambda (_process event)
+       (when (equal "finished\n" event)
+         (let ((tokens (context-coloring-parse-array output)))
+           (with-current-buffer buffer
+             (context-coloring-apply-tokens tokens))
+           (setq context-coloring-scopifier-process nil)
+           (if callback (funcall callback)))))))
+
+  ;; Give the process its input so it can begin.
+  (process-send-region context-coloring-scopifier-process (point-min) (point-max))
+  (process-send-eof context-coloring-scopifier-process))
+
+
+;;; Dispatch
+
+(defvar context-coloring-dispatch-hash-table (make-hash-table :test 'eq)
+  "Mapping of dispatch strategy names to their corresponding
+  property lists, which contain details about the strategies.")
+
+(defvar context-coloring-mode-hash-table (make-hash-table :test 'eq)
+  "Mapping of major mode names to dispatch property lists.")
+
+(defun context-coloring-select-dispatch (mode dispatch)
+  "Use DISPATCH for MODE."
+  (puthash
+   mode
+   (gethash
+    dispatch
+    context-coloring-dispatch-hash-table)
+   context-coloring-mode-hash-table))
+
+(defun context-coloring-define-dispatch (symbol &rest properties)
+  "Define a new dispatch named SYMBOL with PROPERTIES.
+
+A \"dispatch\" is a property list describing a strategy for
+coloring a buffer. There are three possible strategies: Parse and
+color in a single function (`:colorizer'), parse in a function
+that returns scope data (`:scopifier'), or parse with a shell
+command that returns scope data (`:command'). In the latter two
+cases, the scope data will be used to automatically color the
+buffer.
+
+PROPERTIES must include `:modes' and one of `:colorizer',
+`:scopifier' or `:command'.
+
+`:modes' - List of major modes this dispatch is valid for.
+
+`:colorizer' - Symbol referring to a function that parses and
+colors the buffer.
+
+`:scopifier' - Symbol referring to a function that parses the
+buffer a returns a flat vector of start, end and level data.
+
+`:executable' - Optional name of an executable required by
+`:command'.
+
+`:command' - Shell command to execute with the current buffer
+sent via stdin, and with a flat JSON array of start, end and
+level data returned via stdout."
+  (let ((modes (plist-get properties :modes))
+        (colorizer (plist-get properties :colorizer))
+        (scopifier (plist-get properties :scopifier))
+        (command (plist-get properties :command)))
+    (when (null modes)
+      (error "No mode defined for dispatch"))
+    (when (not (or colorizer
+                   scopifier
+                   command))
+      (error "No colorizer, scopifier or command defined for dispatch"))
+    (puthash symbol properties context-coloring-dispatch-hash-table)
+    (dolist (mode modes)
+      (when (null (gethash mode context-coloring-mode-hash-table))
+        (puthash mode properties context-coloring-mode-hash-table)))))
+
+(context-coloring-define-dispatch
+ 'javascript-node
+ :modes '(js-mode js3-mode)
+ :executable "node"
+ :command (expand-file-name
+           "./languages/javascript/binaries/scopifier"
+           context-coloring-path))
+
+(context-coloring-define-dispatch
+ 'javascript-js2
+ :modes '(js2-mode)
+ :colorizer 'context-coloring-js2-colorize)
+
+(defun context-coloring-dispatch (&optional callback)
+  "Determines the optimal track for scopification / colorization
+of the current buffer, then executes it.
+
+Invokes CALLBACK when complete. It is invoked synchronously for
+elisp tracks, and asynchronously for shell command tracks."
+  (let ((dispatch (gethash major-mode context-coloring-mode-hash-table)))
+    (if (null dispatch)
+        (message "%s" "Context coloring is not available for this major mode"))
+    (let (colorizer
+          scopifier
+          command
+          executable)
+      (cond
+       ((setq colorizer (plist-get dispatch :colorizer))
+        (funcall colorizer)
+        (if callback (funcall callback)))
+       ((setq scopifier (plist-get dispatch :scopifier))
+        (context-coloring-apply-tokens (funcall scopifier))
+        (if callback (funcall callback)))
+       ((setq command (plist-get dispatch :command))
+        (setq executable (plist-get dispatch :executable))
+        (if (and (not (null executable))
+                 (null (executable-find executable)))
+            (message "Executable \"%s\" not found" executable)
+          (context-coloring-scopify-shell-command command callback)))))))
+
+
+;;; Colorization
+
+(defun context-coloring-colorize (&optional callback)
+  "Colors the current buffer by function context.
+
+Invokes CALLBACK when complete; see `context-coloring-dispatch'."
+  (interactive)
+  (let ((start-time (float-time)))
+    (context-coloring-dispatch
+     (lambda ()
+       (when context-coloring-benchmark-colorization
+         (message "Colorization took %.3f seconds" (- (float-time) start-time)))
+       (if callback (funcall callback))))))
+
+(defun context-coloring-change-function (_start _end _length)
+  "Registers a change so that a buffer can be colorized soon."
+  ;; Tokenization is obsolete if there was a change.
+  (context-coloring-kill-scopifier)
+  (setq context-coloring-changed t))
+
+(defun context-coloring-maybe-colorize ()
+  "Colorize unders certain conditions.  This will run as an idle
+timer, so firstly the buffer must not be some other buffer.
+Additionally, the buffer must have changed, otherwise colorizing
+would be redundant."
+  (when (and (eq context-coloring-buffer (window-buffer (selected-window)))
+             context-coloring-changed)
+    (setq context-coloring-changed nil)
+    (context-coloring-colorize)))
+
+
+;;; Minor mode
+
+;;;###autoload
+(define-minor-mode context-coloring-mode
+  "Context-based code coloring, inspired by Douglas Crockford."
+  nil " Context" nil
+  (if (not context-coloring-mode)
+      (progn
+        (context-coloring-kill-scopifier)
+        (when context-coloring-colorize-idle-timer
+          (cancel-timer context-coloring-colorize-idle-timer))
+        (remove-hook 'js2-post-parse-callbacks 'context-coloring-colorize t)
+        (remove-hook 'after-change-functions 'context-coloring-change-function t)
+        (font-lock-mode)
+        (jit-lock-mode t))
+
+    ;; Remember this buffer. This value should not be dynamically-bound.
+    (setq context-coloring-buffer (current-buffer))
+
+    ;; Font lock is incompatible with this mode; the converse is also true.
+    (font-lock-mode 0)
+    (jit-lock-mode nil)
+
+    ;; Colorize once initially.
+    (context-coloring-colorize)
+
+    (cond
+     ((equal major-mode 'js2-mode)
+      ;; Only recolor on reparse.
+      (add-hook 'js2-post-parse-callbacks 'context-coloring-colorize nil t))
+     (t
+      ;; Only recolor on change.
+      (add-hook 'after-change-functions 'context-coloring-change-function nil t)))
+
+    (when (not (equal major-mode 'js2-mode))
+      ;; Only recolor idly.
+      (setq context-coloring-colorize-idle-timer
+            (run-with-idle-timer
+             context-coloring-delay
+             t
+             'context-coloring-maybe-colorize)))))
+
+(provide 'context-coloring)
+
+;; Local Variables:
+;; eval: (when (fboundp 'rainbow-mode) (rainbow-mode 1))
+;; End:
+
+;;; context-coloring.el ends here
diff --git a/packages/context-coloring/languages/javascript/.jshintrc b/packages/context-coloring/languages/javascript/.jshintrc
new file mode 100644
index 0000000..82211c2
--- /dev/null
+++ b/packages/context-coloring/languages/javascript/.jshintrc
@@ -0,0 +1,24 @@
+{
+  "bitwise": true,
+  "camelcase": true,
+  "curly": true,
+  "devel": true,
+  "eqeqeq": true,
+  "forin": true,
+  "freeze": true,
+  "funcscope": true,
+  "immed": true,
+  "indent": 4,
+  "latedef": true,
+  "newcap": true,
+  "noarg": true,
+  "node": true,
+  "noempty": true,
+  "nonbsp": true,
+  "nonew": true,
+  "plusplus": true,
+  "quotmark": "single",
+  "strict": true,
+  "undef": true,
+  "unused": "strict"
+}
diff --git a/packages/context-coloring/languages/javascript/binaries/scopifier b/packages/context-coloring/languages/javascript/binaries/scopifier
new file mode 100755
index 0000000..82ea34e
--- /dev/null
+++ b/packages/context-coloring/languages/javascript/binaries/scopifier
@@ -0,0 +1,36 @@
+#!/usr/bin/env node
+
+// Copyright (C) 2014-2015  Free Software Foundation, Inc.
+
+// This file is part of GNU Emacs.
+
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+'use strict';
+
+var scopifier = require('../scopifier'),
+    whole = '';
+
+process.stdin.setEncoding('utf8');
+
+process.stdin.on('readable', function () {
+    var chunk = process.stdin.read();
+    if (chunk !== null) {
+        whole += chunk;
+    }
+});
+
+process.stdin.on('end', function () {
+    console.log(JSON.stringify(scopifier(whole)));
+});
diff --git a/packages/context-coloring/languages/javascript/libraries/escope.js b/packages/context-coloring/languages/javascript/libraries/escope.js
new file mode 100644
index 0000000..b42783c
--- /dev/null
+++ b/packages/context-coloring/languages/javascript/libraries/escope.js
@@ -0,0 +1,1117 @@
+/*
+  Copyright (C) 2012-2013 Yusuke Suzuki <utatane.tea@gmail.com>
+  Copyright (C) 2013 Alex Seville <hi@alexanderseville.com>
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above copyright
+      notice, this list of conditions and the following disclaimer in the
+      documentation and/or other materials provided with the distribution.
+
+  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+  ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
+  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/**
+ * Escope (<a href="http://github.com/Constellation/escope">escope</a>) is an <a
+ * href="http://www.ecma-international.org/publications/standards/Ecma-262.htm">ECMAScript</a>
+ * scope analyzer extracted from the <a
+ * href="http://github.com/Constellation/esmangle">esmangle project</a/>.
+ * <p>
+ * <em>escope</em> finds lexical scopes in a source program, i.e. areas of that
+ * program where different occurrences of the same identifier refer to the same
+ * variable. With each scope the contained variables are collected, and each
+ * identifier reference in code is linked to its corresponding variable (if
+ * possible).
+ * <p>
+ * <em>escope</em> works on a syntax tree of the parsed source code which has
+ * to adhere to the <a
+ * href="https://developer.mozilla.org/en-US/docs/SpiderMonkey/Parser_API">
+ * Mozilla Parser API</a>. E.g. <a href="http://esprima.org">esprima</a> is a parser
+ * that produces such syntax trees.
+ * <p>
+ * The main interface is the {@link analyze} function.
+ * @module
+ */
+
+/*jslint bitwise:true */
+/*global exports:true, define:true, require:true*/
+(function (factory, global) {
+    'use strict';
+
+    function namespace(str, obj) {
+        var i, iz, names, name;
+        names = str.split('.');
+        for (i = 0, iz = names.length; i < iz; ++i) {
+            name = names[i];
+            if (obj.hasOwnProperty(name)) {
+                obj = obj[name];
+            } else {
+                obj = (obj[name] = {});
+            }
+        }
+        return obj;
+    }
+
+    // Universal Module Definition (UMD) to support AMD, CommonJS/Node.js,
+    // and plain browser loading,
+    if (typeof define === 'function' && define.amd) {
+        define('escope', ['exports', 'estraverse'], function (exports, estraverse) {
+            factory(exports, global, estraverse);
+        });
+    } else if (typeof exports !== 'undefined') {
+        factory(exports, global, require('./estraverse'));
+    } else {
+        factory(namespace('escope', global), global, global.estraverse);
+    }
+}(function (exports, global, estraverse) {
+    'use strict';
+
+    var Syntax,
+        Map,
+        currentScope,
+        globalScope,
+        scopes,
+        options;
+
+    Syntax = estraverse.Syntax;
+
+    if (typeof global.Map !== 'undefined') {
+        // ES6 Map
+        Map = global.Map;
+    } else {
+        Map = function Map() {
+            this.__data = {};
+        };
+
+        Map.prototype.get = function MapGet(key) {
+            key = '$' + key;
+            if (this.__data.hasOwnProperty(key)) {
+                return this.__data[key];
+            }
+            return undefined;
+        };
+
+        Map.prototype.has = function MapHas(key) {
+            key = '$' + key;
+            return this.__data.hasOwnProperty(key);
+        };
+
+        Map.prototype.set = function MapSet(key, val) {
+            key = '$' + key;
+            this.__data[key] = val;
+        };
+
+        Map.prototype['delete'] = function MapDelete(key) {
+            key = '$' + key;
+            return delete this.__data[key];
+        };
+    }
+
+    function assert(cond, text) {
+        if (!cond) {
+            throw new Error(text);
+        }
+    }
+
+    function defaultOptions() {
+        return {
+            optimistic: false,
+            directive: false
+        };
+    }
+
+    function updateDeeply(target, override) {
+        var key, val;
+
+        function isHashObject(target) {
+            return typeof target === 'object' && target instanceof Object && !(target instanceof RegExp);
+        }
+
+        for (key in override) {
+            if (override.hasOwnProperty(key)) {
+                val = override[key];
+                if (isHashObject(val)) {
+                    if (isHashObject(target[key])) {
+                        updateDeeply(target[key], val);
+                    } else {
+                        target[key] = updateDeeply({}, val);
+                    }
+                } else {
+                    target[key] = val;
+                }
+            }
+        }
+        return target;
+    }
+
+    /**
+     * A Reference represents a single occurrence of an identifier in code.
+     * @class Reference
+     */
+    function Reference(ident, scope, flag, writeExpr, maybeImplicitGlobal) {
+        /**
+         * Identifier syntax node.
+         * @member {esprima#Identifier} Reference#identifier
+         */
+        this.identifier = ident;
+        /**
+         * Reference to the enclosing Scope.
+         * @member {Scope} Reference#from
+         */
+        this.from = scope;
+        /**
+         * Whether the reference comes from a dynamic scope (such as 'eval',
+         * 'with', etc.), and may be trapped by dynamic scopes.
+         * @member {boolean} Reference#tainted
+         */
+        this.tainted = false;
+        /**
+         * The variable this reference is resolved with.
+         * @member {Variable} Reference#resolved
+         */
+        this.resolved = null;
+        /**
+         * The read-write mode of the reference. (Value is one of {@link
+         * Reference.READ}, {@link Reference.RW}, {@link Reference.WRITE}).
+         * @member {number} Reference#flag
+         * @private
+         */
+        this.flag = flag;
+        if (this.isWrite()) {
+            /**
+             * If reference is writeable, this is the tree being written to it.
+             * @member {esprima#Node} Reference#writeExpr
+             */
+            this.writeExpr = writeExpr;
+        }
+        /**
+         * Whether the Reference might refer to a global variable.
+         * @member {boolean} Reference#__maybeImplicitGlobal
+         * @private
+         */
+        this.__maybeImplicitGlobal = maybeImplicitGlobal;
+    }
+
+    /**
+     * @constant Reference.READ
+     * @private
+     */
+    Reference.READ = 0x1;
+    /**
+     * @constant Reference.WRITE
+     * @private
+     */
+    Reference.WRITE = 0x2;
+    /**
+     * @constant Reference.RW
+     * @private
+     */
+    Reference.RW = 0x3;
+
+    /**
+     * Whether the reference is static.
+     * @method Reference#isStatic
+     * @return {boolean}
+     */
+    Reference.prototype.isStatic = function isStatic() {
+        return !this.tainted && this.resolved && this.resolved.scope.isStatic();
+    };
+
+    /**
+     * Whether the reference is writeable.
+     * @method Reference#isWrite
+     * @return {boolean}
+     */
+    Reference.prototype.isWrite = function isWrite() {
+        return this.flag & Reference.WRITE;
+    };
+
+    /**
+     * Whether the reference is readable.
+     * @method Reference#isRead
+     * @return {boolean}
+     */
+    Reference.prototype.isRead = function isRead() {
+        return this.flag & Reference.READ;
+    };
+
+    /**
+     * Whether the reference is read-only.
+     * @method Reference#isReadOnly
+     * @return {boolean}
+     */
+    Reference.prototype.isReadOnly = function isReadOnly() {
+        return this.flag === Reference.READ;
+    };
+
+    /**
+     * Whether the reference is write-only.
+     * @method Reference#isWriteOnly
+     * @return {boolean}
+     */
+    Reference.prototype.isWriteOnly = function isWriteOnly() {
+        return this.flag === Reference.WRITE;
+    };
+
+    /**
+     * Whether the reference is read-write.
+     * @method Reference#isReadWrite
+     * @return {boolean}
+     */
+    Reference.prototype.isReadWrite = function isReadWrite() {
+        return this.flag === Reference.RW;
+    };
+
+    /**
+     * A Variable represents a locally scoped identifier. These include arguments to
+     * functions.
+     * @class Variable
+     */
+    function Variable(name, scope) {
+        /**
+         * The variable name, as given in the source code.
+         * @member {String} Variable#name
+         */
+        this.name = name;
+        /**
+         * List of defining occurrences of this variable (like in 'var ...'
+         * statements or as parameter), as AST nodes.
+         * @member {esprima.Identifier[]} Variable#identifiers
+         */
+        this.identifiers = [];
+        /**
+         * List of {@link Reference|references} of this variable (excluding parameter entries)
+         * in its defining scope and all nested scopes. For defining
+         * occurrences only see {@link Variable#defs}.
+         * @member {Reference[]} Variable#references
+         */
+        this.references = [];
+
+        /**
+         * List of defining occurrences of this variable (like in 'var ...'
+         * statements or as parameter), as custom objects.
+         * @typedef {Object} DefEntry
+         * @property {String} DefEntry.type - the type of the occurrence (e.g.
+         *      "Parameter", "Variable", ...)
+         * @property {esprima.Identifier} DefEntry.name - the identifier AST node of the occurrence
+         * @property {esprima.Node} DefEntry.node - the enclosing node of the
+         *      identifier
+         * @property {esprima.Node} [DefEntry.parent] - the enclosing statement
+         *      node of the identifier
+         * @member {DefEntry[]} Variable#defs
+         */
+        this.defs = [];
+
+        this.tainted = false;
+        /**
+         * Whether this is a stack variable.
+         * @member {boolean} Variable#stack
+         */
+        this.stack = true;
+        /**
+         * Reference to the enclosing Scope.
+         * @member {Scope} Variable#scope
+         */
+        this.scope = scope;
+    }
+
+    Variable.CatchClause = 'CatchClause';
+    Variable.Parameter = 'Parameter';
+    Variable.FunctionName = 'FunctionName';
+    Variable.Variable = 'Variable';
+    Variable.ImplicitGlobalVariable = 'ImplicitGlobalVariable';
+
+    function isStrictScope(scope, block) {
+        var body, i, iz, stmt, expr;
+
+        // When upper scope is exists and strict, inner scope is also strict.
+        if (scope.upper && scope.upper.isStrict) {
+            return true;
+        }
+
+        if (scope.type === 'function') {
+            body = block.body;
+        } else if (scope.type === 'global') {
+            body = block;
+        } else {
+            return false;
+        }
+
+        if (options.directive) {
+            for (i = 0, iz = body.body.length; i < iz; ++i) {
+                stmt = body.body[i];
+                if (stmt.type !== 'DirectiveStatement') {
+                    break;
+                }
+                if (stmt.raw === '"use strict"' || stmt.raw === '\'use strict\'') {
+                    return true;
+                }
+            }
+        } else {
+            for (i = 0, iz = body.body.length; i < iz; ++i) {
+                stmt = body.body[i];
+                if (stmt.type !== Syntax.ExpressionStatement) {
+                    break;
+                }
+                expr = stmt.expression;
+                if (expr.type !== Syntax.Literal || typeof expr.value !== 'string') {
+                    break;
+                }
+                if (expr.raw != null) {
+                    if (expr.raw === '"use strict"' || expr.raw === '\'use strict\'') {
+                        return true;
+                    }
+                } else {
+                    if (expr.value === 'use strict') {
+                        return true;
+                    }
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
+     * @class Scope
+     */
+    function Scope(block, opt) {
+        var variable, body;
+
+        /**
+         * One of 'catch', 'with', 'function' or 'global'.
+         * @member {String} Scope#type
+         */
+        this.type =
+            (block.type === Syntax.CatchClause) ? 'catch' :
+            (block.type === Syntax.WithStatement) ? 'with' :
+            (block.type === Syntax.Program) ? 'global' : 'function';
+         /**
+         * The scoped {@link Variable}s of this scope, as <code>{ Variable.name
+         * : Variable }</code>.
+         * @member {Map} Scope#set
+         */
+        this.set = new Map();
+        /**
+         * The tainted variables of this scope, as <code>{ Variable.name :
+         * boolean }</code>.
+         * @member {Map} Scope#taints */
+        this.taints = new Map();
+        /**
+         * Generally, through the lexical scoping of JS you can always know
+         * which variable an identifier in the source code refers to. There are
+         * a few exceptions to this rule. With 'global' and 'with' scopes you
+         * can only decide at runtime which variable a reference refers to.
+         * Moreover, if 'eval()' is used in a scope, it might introduce new
+         * bindings in this or its prarent scopes.
+         * All those scopes are considered 'dynamic'.
+         * @member {boolean} Scope#dynamic
+         */
+        this.dynamic = this.type === 'global' || this.type === 'with';
+        /**
+         * A reference to the scope-defining syntax node.
+         * @member {esprima.Node} Scope#block
+         */
+        this.block = block;
+         /**
+         * The {@link Reference|references} that are not resolved with this scope.
+         * @member {Reference[]} Scope#through
+         */
+        this.through = [];
+         /**
+         * The scoped {@link Variable}s of this scope. In the case of a
+         * 'function' scope this includes the automatic argument <em>arguments</em> as
+         * its first element, as well as all further formal arguments.
+         * @member {Variable[]} Scope#variables
+         */
+        this.variables = [];
+         /**
+         * Any variable {@link Reference|reference} found in this scope. This
+         * includes occurrences of local variables as well as variables from
+         * parent scopes (including the global scope). For local variables
+         * this also includes defining occurrences (like in a 'var' statement).
+         * In a 'function' scope this does not include the occurrences of the
+         * formal parameter in the parameter list.
+         * @member {Reference[]} Scope#references
+         */
+        this.references = [];
+         /**
+         * List of {@link Reference}s that are left to be resolved (i.e. which
+         * need to be linked to the variable they refer to). Used internally to
+         * resolve bindings during scope analysis. On a finalized scope
+         * analysis, all sopes have <em>left</em> value <strong>null</strong>.
+         * @member {Reference[]} Scope#left
+         */
+        this.left = [];
+         /**
+         * For 'global' and 'function' scopes, this is a self-reference. For
+         * other scope types this is the <em>variableScope</em> value of the
+         * parent scope.
+         * @member {Scope} Scope#variableScope
+         */
+        this.variableScope =
+            (this.type === 'global' || this.type === 'function') ? this : currentScope.variableScope;
+         /**
+         * Whether this scope is created by a FunctionExpression.
+         * @member {boolean} Scope#functionExpressionScope
+         */
+        this.functionExpressionScope = false;
+         /**
+         * Whether this is a scope that contains an 'eval()' invocation.
+         * @member {boolean} Scope#directCallToEvalScope
+         */
+        this.directCallToEvalScope = false;
+         /**
+         * @member {boolean} Scope#thisFound
+         */
+        this.thisFound = false;
+        body = this.type === 'function' ? block.body : block;
+        if (opt.naming) {
+            this.__define(block.id, {
+                type: Variable.FunctionName,
+                name: block.id,
+                node: block
+            });
+            this.functionExpressionScope = true;
+        } else {
+            if (this.type === 'function') {
+                variable = new Variable('arguments', this);
+                this.taints.set('arguments', true);
+                this.set.set('arguments', variable);
+                this.variables.push(variable);
+            }
+
+            if (block.type === Syntax.FunctionExpression && block.id) {
+                new Scope(block, { naming: true });
+            }
+        }
+
+         /**
+         * Reference to the parent {@link Scope|scope}.
+         * @member {Scope} Scope#upper
+         */
+        this.upper = currentScope;
+         /**
+         * Whether 'use strict' is in effect in this scope.
+         * @member {boolean} Scope#isStrict
+         */
+        this.isStrict = isStrictScope(this, block);
+
+         /**
+         * List of nested {@link Scope}s.
+         * @member {Scope[]} Scope#childScopes
+         */
+        this.childScopes = [];
+        if (currentScope) {
+            currentScope.childScopes.push(this);
+        }
+
+
+        // RAII
+        currentScope = this;
+        if (this.type === 'global') {
+            globalScope = this;
+            globalScope.implicit = {
+                set: new Map(),
+                variables: []
+            };
+        }
+        scopes.push(this);
+    }
+
+    Scope.prototype.__close = function __close() {
+        var i, iz, ref, current, node, implicit;
+
+        // Because if this is global environment, upper is null
+        if (!this.dynamic || options.optimistic) {
+            // static resolve
+            for (i = 0, iz = this.left.length; i < iz; ++i) {
+                ref = this.left[i];
+                if (!this.__resolve(ref)) {
+                    this.__delegateToUpperScope(ref);
+                }
+            }
+        } else {
+            // this is "global" / "with" / "function with eval" environment
+            if (this.type === 'with') {
+                for (i = 0, iz = this.left.length; i < iz; ++i) {
+                    ref = this.left[i];
+                    ref.tainted = true;
+                    this.__delegateToUpperScope(ref);
+                }
+            } else {
+                for (i = 0, iz = this.left.length; i < iz; ++i) {
+                    // notify all names are through to global
+                    ref = this.left[i];
+                    current = this;
+                    do {
+                        current.through.push(ref);
+                        current = current.upper;
+                    } while (current);
+                }
+            }
+        }
+
+        if (this.type === 'global') {
+            implicit = [];
+            for (i = 0, iz = this.left.length; i < iz; ++i) {
+                ref = this.left[i];
+                if (ref.__maybeImplicitGlobal && !this.set.has(ref.identifier.name)) {
+                    implicit.push(ref.__maybeImplicitGlobal);
+                }
+            }
+
+            // create an implicit global variable from assignment expression
+            for (i = 0, iz = implicit.length; i < iz; ++i) {
+                node = implicit[i];
+                this.__defineImplicit(node.left, {
+                    type: Variable.ImplicitGlobalVariable,
+                    name: node.left,
+                    node: node
+                });
+            }
+        }
+
+        this.left = null;
+        currentScope = this.upper;
+    };
+
+    Scope.prototype.__resolve = function __resolve(ref) {
+        var variable, name;
+        name = ref.identifier.name;
+        if (this.set.has(name)) {
+            variable = this.set.get(name);
+            variable.references.push(ref);
+            variable.stack = variable.stack && ref.from.variableScope === this.variableScope;
+            if (ref.tainted) {
+                variable.tainted = true;
+                this.taints.set(variable.name, true);
+            }
+            ref.resolved = variable;
+            return true;
+        }
+        return false;
+    };
+
+    Scope.prototype.__delegateToUpperScope = function __delegateToUpperScope(ref) {
+        if (this.upper) {
+            this.upper.left.push(ref);
+        }
+        this.through.push(ref);
+    };
+
+    Scope.prototype.__defineImplicit = function __defineImplicit(node, info) {
+        var name, variable;
+        if (node && node.type === Syntax.Identifier) {
+            name = node.name;
+            if (!this.implicit.set.has(name)) {
+                variable = new Variable(name, this);
+                variable.identifiers.push(node);
+                variable.defs.push(info);
+                this.implicit.set.set(name, variable);
+                this.implicit.variables.push(variable);
+            } else {
+                variable = this.implicit.set.get(name);
+                variable.identifiers.push(node);
+                variable.defs.push(info);
+            }
+        }
+    };
+
+    Scope.prototype.__define = function __define(node, info) {
+        var name, variable;
+        if (node && node.type === Syntax.Identifier) {
+            name = node.name;
+            if (!this.set.has(name)) {
+                variable = new Variable(name, this);
+                variable.identifiers.push(node);
+                variable.defs.push(info);
+                this.set.set(name, variable);
+                this.variables.push(variable);
+            } else {
+                variable = this.set.get(name);
+                variable.identifiers.push(node);
+                variable.defs.push(info);
+            }
+        }
+    };
+
+    Scope.prototype.__referencing = function __referencing(node, assign, writeExpr, maybeImplicitGlobal) {
+        var ref;
+        // because Array element may be null
+        if (node && node.type === Syntax.Identifier) {
+            ref = new Reference(node, this, assign || Reference.READ, writeExpr, maybeImplicitGlobal);
+            this.references.push(ref);
+            this.left.push(ref);
+        }
+    };
+
+    Scope.prototype.__detectEval = function __detectEval() {
+        var current;
+        current = this;
+        this.directCallToEvalScope = true;
+        do {
+            current.dynamic = true;
+            current = current.upper;
+        } while (current);
+    };
+
+    Scope.prototype.__detectThis = function __detectThis() {
+        this.thisFound = true;
+    };
+
+    Scope.prototype.__isClosed = function isClosed() {
+        return this.left === null;
+    };
+
+    // API Scope#resolve(name)
+    // returns resolved reference
+    Scope.prototype.resolve = function resolve(ident) {
+        var ref, i, iz;
+        assert(this.__isClosed(), 'scope should be closed');
+        assert(ident.type === Syntax.Identifier, 'target should be identifier');
+        for (i = 0, iz = this.references.length; i < iz; ++i) {
+            ref = this.references[i];
+            if (ref.identifier === ident) {
+                return ref;
+            }
+        }
+        return null;
+    };
+
+    // API Scope#isStatic
+    // returns this scope is static
+    Scope.prototype.isStatic = function isStatic() {
+        return !this.dynamic;
+    };
+
+    // API Scope#isArgumentsMaterialized
+    // return this scope has materialized arguments
+    Scope.prototype.isArgumentsMaterialized = function isArgumentsMaterialized() {
+        // TODO(Constellation)
+        // We can more aggressive on this condition like this.
+        //
+        // function t() {
+        //     // arguments of t is always hidden.
+        //     function arguments() {
+        //     }
+        // }
+        var variable;
+
+        // This is not function scope
+        if (this.type !== 'function') {
+            return true;
+        }
+
+        if (!this.isStatic()) {
+            return true;
+        }
+
+        variable = this.set.get('arguments');
+        assert(variable, 'always have arguments variable');
+        return variable.tainted || variable.references.length  !== 0;
+    };
+
+    // API Scope#isThisMaterialized
+    // return this scope has materialized `this` reference
+    Scope.prototype.isThisMaterialized = function isThisMaterialized() {
+        // This is not function scope
+        if (this.type !== 'function') {
+            return true;
+        }
+        if (!this.isStatic()) {
+            return true;
+        }
+        return this.thisFound;
+    };
+
+    Scope.mangledName = '__$escope$__';
+
+    Scope.prototype.attach = function attach() {
+        if (!this.functionExpressionScope) {
+            this.block[Scope.mangledName] = this;
+        }
+    };
+
+    Scope.prototype.detach = function detach() {
+        if (!this.functionExpressionScope) {
+            delete this.block[Scope.mangledName];
+        }
+    };
+
+    Scope.prototype.isUsedName = function (name) {
+        if (this.set.has(name)) {
+            return true;
+        }
+        for (var i = 0, iz = this.through.length; i < iz; ++i) {
+            if (this.through[i].identifier.name === name) {
+                return true;
+            }
+        }
+        return false;
+    };
+
+    /**
+     * @class ScopeManager
+     */
+    function ScopeManager(scopes) {
+        this.scopes = scopes;
+        this.attached = false;
+    }
+
+    // Returns appropliate scope for this node
+    ScopeManager.prototype.__get = function __get(node) {
+        var i, iz, scope;
+        if (this.attached) {
+            return node[Scope.mangledName] || null;
+        }
+        if (Scope.isScopeRequired(node)) {
+            for (i = 0, iz = this.scopes.length; i < iz; ++i) {
+                scope = this.scopes[i];
+                if (!scope.functionExpressionScope) {
+                    if (scope.block === node) {
+                        return scope;
+                    }
+                }
+            }
+        }
+        return null;
+    };
+
+    ScopeManager.prototype.acquire = function acquire(node) {
+        return this.__get(node);
+    };
+
+    ScopeManager.prototype.release = function release(node) {
+        var scope = this.__get(node);
+        if (scope) {
+            scope = scope.upper;
+            while (scope) {
+                if (!scope.functionExpressionScope) {
+                    return scope;
+                }
+                scope = scope.upper;
+            }
+        }
+        return null;
+    };
+
+    ScopeManager.prototype.attach = function attach() {
+        var i, iz;
+        for (i = 0, iz = this.scopes.length; i < iz; ++i) {
+            this.scopes[i].attach();
+        }
+        this.attached = true;
+    };
+
+    ScopeManager.prototype.detach = function detach() {
+        var i, iz;
+        for (i = 0, iz = this.scopes.length; i < iz; ++i) {
+            this.scopes[i].detach();
+        }
+        this.attached = false;
+    };
+
+    Scope.isScopeRequired = function isScopeRequired(node) {
+        return Scope.isVariableScopeRequired(node) || node.type === Syntax.WithStatement || node.type === Syntax.CatchClause;
+    };
+
+    Scope.isVariableScopeRequired = function isVariableScopeRequired(node) {
+        return node.type === Syntax.Program || node.type === Syntax.FunctionExpression || node.type === Syntax.FunctionDeclaration;
+    };
+
+    /**
+     * Main interface function. Takes an Esprima syntax tree and returns the
+     * analyzed scopes.
+     * @function analyze
+     * @param {esprima.Tree} tree
+     * @param {Object} providedOptions - Options that tailor the scope analysis
+     * @param {boolean} [providedOptions.optimistic=false] - the optimistic flag
+     * @param {boolean} [providedOptions.directive=false]- the directive flag
+     * @param {boolean} [providedOptions.ignoreEval=false]- whether to check 'eval()' calls
+     * @return {ScopeManager}
+     */
+    function analyze(tree, providedOptions) {
+        var resultScopes;
+
+        options = updateDeeply(defaultOptions(), providedOptions);
+        resultScopes = scopes = [];
+        currentScope = null;
+        globalScope = null;
+
+        // attach scope and collect / resolve names
+        estraverse.traverse(tree, {
+            enter: function enter(node) {
+                var i, iz, decl;
+                if (Scope.isScopeRequired(node)) {
+                    new Scope(node, {});
+                }
+
+                switch (node.type) {
+                case Syntax.AssignmentExpression:
+                    if (node.operator === '=') {
+                        currentScope.__referencing(node.left, Reference.WRITE, node.right, (!currentScope.isStrict && node.left.name != null) && node);
+                    } else {
+                        currentScope.__referencing(node.left, Reference.RW, node.right);
+                    }
+                    currentScope.__referencing(node.right);
+                    break;
+
+                case Syntax.ArrayExpression:
+                    for (i = 0, iz = node.elements.length; i < iz; ++i) {
+                        currentScope.__referencing(node.elements[i]);
+                    }
+                    break;
+
+                case Syntax.BlockStatement:
+                    break;
+
+                case Syntax.BinaryExpression:
+                    currentScope.__referencing(node.left);
+                    currentScope.__referencing(node.right);
+                    break;
+
+                case Syntax.BreakStatement:
+                    break;
+
+                case Syntax.CallExpression:
+                    currentScope.__referencing(node.callee);
+                    for (i = 0, iz = node['arguments'].length; i < iz; ++i) {
+                        currentScope.__referencing(node['arguments'][i]);
+                    }
+
+                    // check this is direct call to eval
+                    if (!options.ignoreEval && node.callee.type === Syntax.Identifier && node.callee.name === 'eval') {
+                        currentScope.variableScope.__detectEval();
+                    }
+                    break;
+
+                case Syntax.CatchClause:
+                    currentScope.__define(node.param, {
+                        type: Variable.CatchClause,
+                        name: node.param,
+                        node: node
+                    });
+                    break;
+
+                case Syntax.ConditionalExpression:
+                    currentScope.__referencing(node.test);
+                    currentScope.__referencing(node.consequent);
+                    currentScope.__referencing(node.alternate);
+                    break;
+
+                case Syntax.ContinueStatement:
+                    break;
+
+                case Syntax.DirectiveStatement:
+                    break;
+
+                case Syntax.DoWhileStatement:
+                    currentScope.__referencing(node.test);
+                    break;
+
+                case Syntax.DebuggerStatement:
+                    break;
+
+                case Syntax.EmptyStatement:
+                    break;
+
+                case Syntax.ExpressionStatement:
+                    currentScope.__referencing(node.expression);
+                    break;
+
+                case Syntax.ForStatement:
+                    currentScope.__referencing(node.init);
+                    currentScope.__referencing(node.test);
+                    currentScope.__referencing(node.update);
+                    break;
+
+                case Syntax.ForInStatement:
+                    if (node.left.type === Syntax.VariableDeclaration) {
+                        currentScope.__referencing(node.left.declarations[0].id, Reference.WRITE, null, false);
+                    } else {
+                        currentScope.__referencing(node.left, Reference.WRITE, null, (!currentScope.isStrict && node.left.name != null) && node);
+                    }
+                    currentScope.__referencing(node.right);
+                    break;
+
+                case Syntax.FunctionDeclaration:
+                    // FunctionDeclaration name is defined in upper scope
+                    currentScope.upper.__define(node.id, {
+                        type: Variable.FunctionName,
+                        name: node.id,
+                        node: node
+                    });
+                    for (i = 0, iz = node.params.length; i < iz; ++i) {
+                        currentScope.__define(node.params[i], {
+                            type: Variable.Parameter,
+                            name: node.params[i],
+                            node: node,
+                            index: i
+                        });
+                    }
+                    break;
+
+                case Syntax.FunctionExpression:
+                    // id is defined in upper scope
+                    for (i = 0, iz = node.params.length; i < iz; ++i) {
+                        currentScope.__define(node.params[i], {
+                            type: Variable.Parameter,
+                            name: node.params[i],
+                            node: node,
+                            index: i
+                        });
+                    }
+                    break;
+
+                case Syntax.Identifier:
+                    break;
+
+                case Syntax.IfStatement:
+                    currentScope.__referencing(node.test);
+                    break;
+
+                case Syntax.Literal:
+                    break;
+
+                case Syntax.LabeledStatement:
+                    break;
+
+                case Syntax.LogicalExpression:
+                    currentScope.__referencing(node.left);
+                    currentScope.__referencing(node.right);
+                    break;
+
+                case Syntax.MemberExpression:
+                    currentScope.__referencing(node.object);
+                    if (node.computed) {
+                        currentScope.__referencing(node.property);
+                    }
+                    break;
+
+                case Syntax.NewExpression:
+                    currentScope.__referencing(node.callee);
+                    for (i = 0, iz = node['arguments'].length; i < iz; ++i) {
+                        currentScope.__referencing(node['arguments'][i]);
+                    }
+                    break;
+
+                case Syntax.ObjectExpression:
+                    break;
+
+                case Syntax.Program:
+                    break;
+
+                case Syntax.Property:
+                    currentScope.__referencing(node.value);
+                    break;
+
+                case Syntax.ReturnStatement:
+                    currentScope.__referencing(node.argument);
+                    break;
+
+                case Syntax.SequenceExpression:
+                    for (i = 0, iz = node.expressions.length; i < iz; ++i) {
+                        currentScope.__referencing(node.expressions[i]);
+                    }
+                    break;
+
+                case Syntax.SwitchStatement:
+                    currentScope.__referencing(node.discriminant);
+                    break;
+
+                case Syntax.SwitchCase:
+                    currentScope.__referencing(node.test);
+                    break;
+
+                case Syntax.ThisExpression:
+                    currentScope.variableScope.__detectThis();
+                    break;
+
+                case Syntax.ThrowStatement:
+                    currentScope.__referencing(node.argument);
+                    break;
+
+                case Syntax.TryStatement:
+                    break;
+
+                case Syntax.UnaryExpression:
+                    currentScope.__referencing(node.argument);
+                    break;
+
+                case Syntax.UpdateExpression:
+                    currentScope.__referencing(node.argument, Reference.RW, null);
+                    break;
+
+                case Syntax.VariableDeclaration:
+                    for (i = 0, iz = node.declarations.length; i < iz; ++i) {
+                        decl = node.declarations[i];
+                        currentScope.variableScope.__define(decl.id, {
+                            type: Variable.Variable,
+                            name: decl.id,
+                            node: decl,
+                            index: i,
+                            parent: node
+                        });
+                        if (decl.init) {
+                            // initializer is found
+                            currentScope.__referencing(decl.id, Reference.WRITE, decl.init, false);
+                            currentScope.__referencing(decl.init);
+                        }
+                    }
+                    break;
+
+                case Syntax.VariableDeclarator:
+                    break;
+
+                case Syntax.WhileStatement:
+                    currentScope.__referencing(node.test);
+                    break;
+
+                case Syntax.WithStatement:
+                    // WithStatement object is referenced at upper scope
+                    currentScope.upper.__referencing(node.object);
+                    break;
+                }
+            },
+
+            leave: function leave(node) {
+                while (currentScope && node === currentScope.block) {
+                    currentScope.__close();
+                }
+            }
+        });
+
+        assert(currentScope === null);
+        globalScope = null;
+        scopes = null;
+        options = null;
+
+        return new ScopeManager(resultScopes);
+    }
+
+    /** @name module:escope.version */
+    exports.version = '1.0.1';
+    /** @name module:escope.Reference */
+    exports.Reference = Reference;
+    /** @name module:escope.Variable */
+    exports.Variable = Variable;
+    /** @name module:escope.Scope */
+    exports.Scope = Scope;
+    /** @name module:escope.ScopeManager */
+    exports.ScopeManager = ScopeManager;
+    /** @name module:escope.analyze */
+    exports.analyze = analyze;
+}, this));
+/* vim: set sw=4 ts=4 et tw=80 : */
diff --git a/packages/context-coloring/languages/javascript/libraries/esprima.js b/packages/context-coloring/languages/javascript/libraries/esprima.js
new file mode 100644
index 0000000..593021f
--- /dev/null
+++ b/packages/context-coloring/languages/javascript/libraries/esprima.js
@@ -0,0 +1,3756 @@
+/*
+  Copyright (C) 2013 Ariya Hidayat <ariya.hidayat@gmail.com>
+  Copyright (C) 2013 Thaddee Tyl <thaddee.tyl@gmail.com>
+  Copyright (C) 2013 Mathias Bynens <mathias@qiwi.be>
+  Copyright (C) 2012 Ariya Hidayat <ariya.hidayat@gmail.com>
+  Copyright (C) 2012 Mathias Bynens <mathias@qiwi.be>
+  Copyright (C) 2012 Joost-Wim Boekesteijn <joost-wim@boekesteijn.nl>
+  Copyright (C) 2012 Kris Kowal <kris.kowal@cixar.com>
+  Copyright (C) 2012 Yusuke Suzuki <utatane.tea@gmail.com>
+  Copyright (C) 2012 Arpad Borsos <arpad.borsos@googlemail.com>
+  Copyright (C) 2011 Ariya Hidayat <ariya.hidayat@gmail.com>
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above copyright
+      notice, this list of conditions and the following disclaimer in the
+      documentation and/or other materials provided with the distribution.
+
+  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+  ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
+  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/*jslint bitwise:true plusplus:true */
+/*global esprima:true, define:true, exports:true, window: true,
+throwErrorTolerant: true,
+throwError: true, generateStatement: true, peek: true,
+parseAssignmentExpression: true, parseBlock: true, parseExpression: true,
+parseFunctionDeclaration: true, parseFunctionExpression: true,
+parseFunctionSourceElements: true, parseVariableIdentifier: true,
+parseLeftHandSideExpression: true,
+parseUnaryExpression: true,
+parseStatement: true, parseSourceElement: true */
+
+(function (root, factory) {
+    'use strict';
+
+    // Universal Module Definition (UMD) to support AMD, CommonJS/Node.js,
+    // Rhino, and plain browser loading.
+
+    /* istanbul ignore next */
+    if (typeof define === 'function' && define.amd) {
+        define(['exports'], factory);
+    } else if (typeof exports !== 'undefined') {
+        factory(exports);
+    } else {
+        factory((root.esprima = {}));
+    }
+}(this, function (exports) {
+    'use strict';
+
+    var Token,
+        TokenName,
+        FnExprTokens,
+        Syntax,
+        PropertyKind,
+        Messages,
+        Regex,
+        SyntaxTreeDelegate,
+        source,
+        strict,
+        index,
+        lineNumber,
+        lineStart,
+        length,
+        delegate,
+        lookahead,
+        state,
+        extra;
+
+    Token = {
+        BooleanLiteral: 1,
+        EOF: 2,
+        Identifier: 3,
+        Keyword: 4,
+        NullLiteral: 5,
+        NumericLiteral: 6,
+        Punctuator: 7,
+        StringLiteral: 8,
+        RegularExpression: 9
+    };
+
+    TokenName = {};
+    TokenName[Token.BooleanLiteral] = 'Boolean';
+    TokenName[Token.EOF] = '<end>';
+    TokenName[Token.Identifier] = 'Identifier';
+    TokenName[Token.Keyword] = 'Keyword';
+    TokenName[Token.NullLiteral] = 'Null';
+    TokenName[Token.NumericLiteral] = 'Numeric';
+    TokenName[Token.Punctuator] = 'Punctuator';
+    TokenName[Token.StringLiteral] = 'String';
+    TokenName[Token.RegularExpression] = 'RegularExpression';
+
+    // A function following one of those tokens is an expression.
+    FnExprTokens = ['(', '{', '[', 'in', 'typeof', 'instanceof', 'new',
+                    'return', 'case', 'delete', 'throw', 'void',
+                    // assignment operators
+                    '=', '+=', '-=', '*=', '/=', '%=', '<<=', '>>=', '>>>=',
+                    '&=', '|=', '^=', ',',
+                    // binary/unary operators
+                    '+', '-', '*', '/', '%', '++', '--', '<<', '>>', '>>>', '&',
+                    '|', '^', '!', '~', '&&', '||', '?', ':', '===', '==', '>=',
+                    '<=', '<', '>', '!=', '!=='];
+
+    Syntax = {
+        AssignmentExpression: 'AssignmentExpression',
+        ArrayExpression: 'ArrayExpression',
+        BlockStatement: 'BlockStatement',
+        BinaryExpression: 'BinaryExpression',
+        BreakStatement: 'BreakStatement',
+        CallExpression: 'CallExpression',
+        CatchClause: 'CatchClause',
+        ConditionalExpression: 'ConditionalExpression',
+        ContinueStatement: 'ContinueStatement',
+        DoWhileStatement: 'DoWhileStatement',
+        DebuggerStatement: 'DebuggerStatement',
+        EmptyStatement: 'EmptyStatement',
+        ExpressionStatement: 'ExpressionStatement',
+        ForStatement: 'ForStatement',
+        ForInStatement: 'ForInStatement',
+        FunctionDeclaration: 'FunctionDeclaration',
+        FunctionExpression: 'FunctionExpression',
+        Identifier: 'Identifier',
+        IfStatement: 'IfStatement',
+        Literal: 'Literal',
+        LabeledStatement: 'LabeledStatement',
+        LogicalExpression: 'LogicalExpression',
+        MemberExpression: 'MemberExpression',
+        NewExpression: 'NewExpression',
+        ObjectExpression: 'ObjectExpression',
+        Program: 'Program',
+        Property: 'Property',
+        ReturnStatement: 'ReturnStatement',
+        SequenceExpression: 'SequenceExpression',
+        SwitchStatement: 'SwitchStatement',
+        SwitchCase: 'SwitchCase',
+        ThisExpression: 'ThisExpression',
+        ThrowStatement: 'ThrowStatement',
+        TryStatement: 'TryStatement',
+        UnaryExpression: 'UnaryExpression',
+        UpdateExpression: 'UpdateExpression',
+        VariableDeclaration: 'VariableDeclaration',
+        VariableDeclarator: 'VariableDeclarator',
+        WhileStatement: 'WhileStatement',
+        WithStatement: 'WithStatement'
+    };
+
+    PropertyKind = {
+        Data: 1,
+        Get: 2,
+        Set: 4
+    };
+
+    // Error messages should be identical to V8.
+    Messages = {
+        UnexpectedToken:  'Unexpected token %0',
+        UnexpectedNumber:  'Unexpected number',
+        UnexpectedString:  'Unexpected string',
+        UnexpectedIdentifier:  'Unexpected identifier',
+        UnexpectedReserved:  'Unexpected reserved word',
+        UnexpectedEOS:  'Unexpected end of input',
+        NewlineAfterThrow:  'Illegal newline after throw',
+        InvalidRegExp: 'Invalid regular expression',
+        UnterminatedRegExp:  'Invalid regular expression: missing /',
+        InvalidLHSInAssignment:  'Invalid left-hand side in assignment',
+        InvalidLHSInForIn:  'Invalid left-hand side in for-in',
+        MultipleDefaultsInSwitch: 'More than one default clause in switch statement',
+        NoCatchOrFinally:  'Missing catch or finally after try',
+        UnknownLabel: 'Undefined label \'%0\'',
+        Redeclaration: '%0 \'%1\' has already been declared',
+        IllegalContinue: 'Illegal continue statement',
+        IllegalBreak: 'Illegal break statement',
+        IllegalReturn: 'Illegal return statement',
+        StrictModeWith:  'Strict mode code may not include a with statement',
+        StrictCatchVariable:  'Catch variable may not be eval or arguments in strict mode',
+        StrictVarName:  'Variable name may not be eval or arguments in strict mode',
+        StrictParamName:  'Parameter name eval or arguments is not allowed in strict mode',
+        StrictParamDupe: 'Strict mode function may not have duplicate parameter names',
+        StrictFunctionName:  'Function name may not be eval or arguments in strict mode',
+        StrictOctalLiteral:  'Octal literals are not allowed in strict mode.',
+        StrictDelete:  'Delete of an unqualified identifier in strict mode.',
+        StrictDuplicateProperty:  'Duplicate data property in object literal not allowed in strict mode',
+        AccessorDataProperty:  'Object literal may not have data and accessor property with the same name',
+        AccessorGetSet:  'Object literal may not have multiple get/set accessors with the same name',
+        StrictLHSAssignment:  'Assignment to eval or arguments is not allowed in strict mode',
+        StrictLHSPostfix:  'Postfix increment/decrement may not have eval or arguments operand in strict mode',
+        StrictLHSPrefix:  'Prefix increment/decrement may not have eval or arguments operand in strict mode',
+        StrictReservedWord:  'Use of future reserved word in strict mode'
+    };
+
+    // See also tools/generate-unicode-regex.py.
+    Regex = {
+        NonAsciiIdentifierStart: new RegExp('[\xAA\xB5\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377\u037A-\u037D\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u0527\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA\u05F0-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u08A0\u08A2-\u08AC\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0977\u0979-\u097F\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C33\u0C35-\u0C39\u0C3D\u0C58\u0C59\u0C60\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D\u0D4E\u0D60\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F4\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16EE-\u16F0\u1700-\u170C\u170E-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u1820-\u1877\u1880-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191C\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19C1-\u19C7\u1A00-\u1A16\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE\u1BAF\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1CE9-\u1CEC\u1CEE-\u1CF1\u1CF5\u1CF6\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2160-\u2188\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2E2F\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303C\u3041-\u3096\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FCC\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B\uA640-\uA66E\uA67F-\uA697\uA6A0-\uA6EF\uA717-\uA71F\uA722-\uA788\uA78B-\uA78E\uA790-\uA793\uA7A0-\uA7AA\uA7F8-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA80-\uAAAF\uAAB1\uAAB5\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uABC0-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC]'),
+        NonAsciiIdentifierPart: new RegExp('[\xAA\xB5\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0300-\u0374\u0376\u0377\u037A-\u037D\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u0483-\u0487\u048A-\u0527\u0531-\u0556\u0559\u0561-\u0587\u0591-\u05BD\u05BF\u05C1\u05C2\u05C4\u05C5\u05C7\u05D0-\u05EA\u05F0-\u05F2\u0610-\u061A\u0620-\u0669\u066E-\u06D3\u06D5-\u06DC\u06DF-\u06E8\u06EA-\u06FC\u06FF\u0710-\u074A\u074D-\u07B1\u07C0-\u07F5\u07FA\u0800-\u082D\u0840-\u085B\u08A0\u08A2-\u08AC\u08E4-\u08FE\u0900-\u0963\u0966-\u096F\u0971-\u0977\u0979-\u097F\u0981-\u0983\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BC-\u09C4\u09C7\u09C8\u09CB-\u09CE\u09D7\u09DC\u09DD\u09DF-\u09E3\u09E6-\u09F1\u0A01-\u0A03\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A3C\u0A3E-\u0A42\u0A47\u0A48\u0A4B-\u0A4D\u0A51\u0A59-\u0A5C\u0A5E\u0A66-\u0A75\u0A81-\u0A83\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABC-\u0AC5\u0AC7-\u0AC9\u0ACB-\u0ACD\u0AD0\u0AE0-\u0AE3\u0AE6-\u0AEF\u0B01-\u0B03\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3C-\u0B44\u0B47\u0B48\u0B4B-\u0B4D\u0B56\u0B57\u0B5C\u0B5D\u0B5F-\u0B63\u0B66-\u0B6F\u0B71\u0B82\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BBE-\u0BC2\u0BC6-\u0BC8\u0BCA-\u0BCD\u0BD0\u0BD7\u0BE6-\u0BEF\u0C01-\u0C03\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C33\u0C35-\u0C39\u0C3D-\u0C44\u0C46-\u0C48\u0C4A-\u0C4D\u0C55\u0C56\u0C58\u0C59\u0C60-\u0C63\u0C66-\u0C6F\u0C82\u0C83\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBC-\u0CC4\u0CC6-\u0CC8\u0CCA-\u0CCD\u0CD5\u0CD6\u0CDE\u0CE0-\u0CE3\u0CE6-\u0CEF\u0CF1\u0CF2\u0D02\u0D03\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D-\u0D44\u0D46-\u0D48\u0D4A-\u0D4E\u0D57\u0D60-\u0D63\u0D66-\u0D6F\u0D7A-\u0D7F\u0D82\u0D83\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0DCA\u0DCF-\u0DD4\u0DD6\u0DD8-\u0DDF\u0DF2\u0DF3\u0E01-\u0E3A\u0E40-\u0E4E\u0E50-\u0E59\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB9\u0EBB-\u0EBD\u0EC0-\u0EC4\u0EC6\u0EC8-\u0ECD\u0ED0-\u0ED9\u0EDC-\u0EDF\u0F00\u0F18\u0F19\u0F20-\u0F29\u0F35\u0F37\u0F39\u0F3E-\u0F47\u0F49-\u0F6C\u0F71-\u0F84\u0F86-\u0F97\u0F99-\u0FBC\u0FC6\u1000-\u1049\u1050-\u109D\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u135D-\u135F\u1380-\u138F\u13A0-\u13F4\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16EE-\u16F0\u1700-\u170C\u170E-\u1714\u1720-\u1734\u1740-\u1753\u1760-\u176C\u176E-\u1770\u1772\u1773\u1780-\u17D3\u17D7\u17DC\u17DD\u17E0-\u17E9\u180B-\u180D\u1810-\u1819\u1820-\u1877\u1880-\u18AA\u18B0-\u18F5\u1900-\u191C\u1920-\u192B\u1930-\u193B\u1946-\u196D\u1970-\u1974\u1980-\u19AB\u19B0-\u19C9\u19D0-\u19D9\u1A00-\u1A1B\u1A20-\u1A5E\u1A60-\u1A7C\u1A7F-\u1A89\u1A90-\u1A99\u1AA7\u1B00-\u1B4B\u1B50-\u1B59\u1B6B-\u1B73\u1B80-\u1BF3\u1C00-\u1C37\u1C40-\u1C49\u1C4D-\u1C7D\u1CD0-\u1CD2\u1CD4-\u1CF6\u1D00-\u1DE6\u1DFC-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u200C\u200D\u203F\u2040\u2054\u2071\u207F\u2090-\u209C\u20D0-\u20DC\u20E1\u20E5-\u20F0\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2160-\u2188\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D7F-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2DE0-\u2DFF\u2E2F\u3005-\u3007\u3021-\u302F\u3031-\u3035\u3038-\u303C\u3041-\u3096\u3099\u309A\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FCC\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA62B\uA640-\uA66F\uA674-\uA67D\uA67F-\uA697\uA69F-\uA6F1\uA717-\uA71F\uA722-\uA788\uA78B-\uA78E\uA790-\uA793\uA7A0-\uA7AA\uA7F8-\uA827\uA840-\uA873\uA880-\uA8C4\uA8D0-\uA8D9\uA8E0-\uA8F7\uA8FB\uA900-\uA92D\uA930-\uA953\uA960-\uA97C\uA980-\uA9C0\uA9CF-\uA9D9\uAA00-\uAA36\uAA40-\uAA4D\uAA50-\uAA59\uAA60-\uAA76\uAA7A\uAA7B\uAA80-\uAAC2\uAADB-\uAADD\uAAE0-\uAAEF\uAAF2-\uAAF6\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uABC0-\uABEA\uABEC\uABED\uABF0-\uABF9\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE00-\uFE0F\uFE20-\uFE26\uFE33\uFE34\uFE4D-\uFE4F\uFE70-\uFE74\uFE76-\uFEFC\uFF10-\uFF19\uFF21-\uFF3A\uFF3F\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC]')
+    };
+
+    // Ensure the condition is true, otherwise throw an error.
+    // This is only to have a better contract semantic, i.e. another safety net
+    // to catch a logic error. The condition shall be fulfilled in normal case.
+    // Do NOT use this to enforce a certain condition on any user input.
+
+    function assert(condition, message) {
+        /* istanbul ignore if */
+        if (!condition) {
+            throw new Error('ASSERT: ' + message);
+        }
+    }
+
+    function isDecimalDigit(ch) {
+        return (ch >= 48 && ch <= 57);   // 0..9
+    }
+
+    function isHexDigit(ch) {
+        return '0123456789abcdefABCDEF'.indexOf(ch) >= 0;
+    }
+
+    function isOctalDigit(ch) {
+        return '01234567'.indexOf(ch) >= 0;
+    }
+
+
+    // 7.2 White Space
+
+    function isWhiteSpace(ch) {
+        return (ch === 0x20) || (ch === 0x09) || (ch === 0x0B) || (ch === 0x0C) || (ch === 0xA0) ||
+            (ch >= 0x1680 && [0x1680, 0x180E, 0x2000, 0x2001, 0x2002, 0x2003, 0x2004, 0x2005, 0x2006, 0x2007, 0x2008, 0x2009, 0x200A, 0x202F, 0x205F, 0x3000, 0xFEFF].indexOf(ch) >= 0);
+    }
+
+    // 7.3 Line Terminators
+
+    function isLineTerminator(ch) {
+        return (ch === 0x0A) || (ch === 0x0D) || (ch === 0x2028) || (ch === 0x2029);
+    }
+
+    // 7.6 Identifier Names and Identifiers
+
+    function isIdentifierStart(ch) {
+        return (ch === 0x24) || (ch === 0x5F) ||  // $ (dollar) and _ (underscore)
+            (ch >= 0x41 && ch <= 0x5A) ||         // A..Z
+            (ch >= 0x61 && ch <= 0x7A) ||         // a..z
+            (ch === 0x5C) ||                      // \ (backslash)
+            ((ch >= 0x80) && Regex.NonAsciiIdentifierStart.test(String.fromCharCode(ch)));
+    }
+
+    function isIdentifierPart(ch) {
+        return (ch === 0x24) || (ch === 0x5F) ||  // $ (dollar) and _ (underscore)
+            (ch >= 0x41 && ch <= 0x5A) ||         // A..Z
+            (ch >= 0x61 && ch <= 0x7A) ||         // a..z
+            (ch >= 0x30 && ch <= 0x39) ||         // 0..9
+            (ch === 0x5C) ||                      // \ (backslash)
+            ((ch >= 0x80) && Regex.NonAsciiIdentifierPart.test(String.fromCharCode(ch)));
+    }
+
+    // 7.6.1.2 Future Reserved Words
+
+    function isFutureReservedWord(id) {
+        switch (id) {
+        case 'class':
+        case 'enum':
+        case 'export':
+        case 'extends':
+        case 'import':
+        case 'super':
+            return true;
+        default:
+            return false;
+        }
+    }
+
+    function isStrictModeReservedWord(id) {
+        switch (id) {
+        case 'implements':
+        case 'interface':
+        case 'package':
+        case 'private':
+        case 'protected':
+        case 'public':
+        case 'static':
+        case 'yield':
+        case 'let':
+            return true;
+        default:
+            return false;
+        }
+    }
+
+    function isRestrictedWord(id) {
+        return id === 'eval' || id === 'arguments';
+    }
+
+    // 7.6.1.1 Keywords
+
+    function isKeyword(id) {
+        if (strict && isStrictModeReservedWord(id)) {
+            return true;
+        }
+
+        // 'const' is specialized as Keyword in V8.
+        // 'yield' and 'let' are for compatiblity with SpiderMonkey and ES.next.
+        // Some others are from future reserved words.
+
+        switch (id.length) {
+        case 2:
+            return (id === 'if') || (id === 'in') || (id === 'do');
+        case 3:
+            return (id === 'var') || (id === 'for') || (id === 'new') ||
+                (id === 'try') || (id === 'let');
+        case 4:
+            return (id === 'this') || (id === 'else') || (id === 'case') ||
+                (id === 'void') || (id === 'with') || (id === 'enum');
+        case 5:
+            return (id === 'while') || (id === 'break') || (id === 'catch') ||
+                (id === 'throw') || (id === 'const') || (id === 'yield') ||
+                (id === 'class') || (id === 'super');
+        case 6:
+            return (id === 'return') || (id === 'typeof') || (id === 'delete') ||
+                (id === 'switch') || (id === 'export') || (id === 'import');
+        case 7:
+            return (id === 'default') || (id === 'finally') || (id === 'extends');
+        case 8:
+            return (id === 'function') || (id === 'continue') || (id === 'debugger');
+        case 10:
+            return (id === 'instanceof');
+        default:
+            return false;
+        }
+    }
+
+    // 7.4 Comments
+
+    function addComment(type, value, start, end, loc) {
+        var comment, attacher;
+
+        assert(typeof start === 'number', 'Comment must have valid position');
+
+        // Because the way the actual token is scanned, often the comments
+        // (if any) are skipped twice during the lexical analysis.
+        // Thus, we need to skip adding a comment if the comment array already
+        // handled it.
+        if (state.lastCommentStart >= start) {
+            return;
+        }
+        state.lastCommentStart = start;
+
+        comment = {
+            type: type,
+            value: value
+        };
+        if (extra.range) {
+            comment.range = [start, end];
+        }
+        if (extra.loc) {
+            comment.loc = loc;
+        }
+        extra.comments.push(comment);
+        if (extra.attachComment) {
+            extra.leadingComments.push(comment);
+            extra.trailingComments.push(comment);
+        }
+    }
+
+    function skipSingleLineComment(offset) {
+        var start, loc, ch, comment;
+
+        start = index - offset;
+        loc = {
+            start: {
+                line: lineNumber,
+                column: index - lineStart - offset
+            }
+        };
+
+        while (index < length) {
+            ch = source.charCodeAt(index);
+            ++index;
+            if (isLineTerminator(ch)) {
+                if (extra.comments) {
+                    comment = source.slice(start + offset, index - 1);
+                    loc.end = {
+                        line: lineNumber,
+                        column: index - lineStart - 1
+                    };
+                    addComment('Line', comment, start, index - 1, loc);
+                }
+                if (ch === 13 && source.charCodeAt(index) === 10) {
+                    ++index;
+                }
+                ++lineNumber;
+                lineStart = index;
+                return;
+            }
+        }
+
+        if (extra.comments) {
+            comment = source.slice(start + offset, index);
+            loc.end = {
+                line: lineNumber,
+                column: index - lineStart
+            };
+            addComment('Line', comment, start, index, loc);
+        }
+    }
+
+    function skipMultiLineComment() {
+        var start, loc, ch, comment;
+
+        if (extra.comments) {
+            start = index - 2;
+            loc = {
+                start: {
+                    line: lineNumber,
+                    column: index - lineStart - 2
+                }
+            };
+        }
+
+        while (index < length) {
+            ch = source.charCodeAt(index);
+            if (isLineTerminator(ch)) {
+                if (ch === 0x0D && source.charCodeAt(index + 1) === 0x0A) {
+                    ++index;
+                }
+                ++lineNumber;
+                ++index;
+                lineStart = index;
+                if (index >= length) {
+                    throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
+                }
+            } else if (ch === 0x2A) {
+                // Block comment ends with '*/'.
+                if (source.charCodeAt(index + 1) === 0x2F) {
+                    ++index;
+                    ++index;
+                    if (extra.comments) {
+                        comment = source.slice(start + 2, index - 2);
+                        loc.end = {
+                            line: lineNumber,
+                            column: index - lineStart
+                        };
+                        addComment('Block', comment, start, index, loc);
+                    }
+                    return;
+                }
+                ++index;
+            } else {
+                ++index;
+            }
+        }
+
+        throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
+    }
+
+    function skipComment() {
+        var ch, start;
+
+        start = (index === 0);
+        while (index < length) {
+            ch = source.charCodeAt(index);
+
+            if (isWhiteSpace(ch)) {
+                ++index;
+            } else if (isLineTerminator(ch)) {
+                ++index;
+                if (ch === 0x0D && source.charCodeAt(index) === 0x0A) {
+                    ++index;
+                }
+                ++lineNumber;
+                lineStart = index;
+                start = true;
+            } else if (ch === 0x2F) { // U+002F is '/'
+                ch = source.charCodeAt(index + 1);
+                if (ch === 0x2F) {
+                    ++index;
+                    ++index;
+                    skipSingleLineComment(2);
+                    start = true;
+                } else if (ch === 0x2A) {  // U+002A is '*'
+                    ++index;
+                    ++index;
+                    skipMultiLineComment();
+                } else {
+                    break;
+                }
+            } else if (start && ch === 0x2D) { // U+002D is '-'
+                // U+003E is '>'
+                if ((source.charCodeAt(index + 1) === 0x2D) && (source.charCodeAt(index + 2) === 0x3E)) {
+                    // '-->' is a single-line comment
+                    index += 3;
+                    skipSingleLineComment(3);
+                } else {
+                    break;
+                }
+            } else if (ch === 0x3C) { // U+003C is '<'
+                if (source.slice(index + 1, index + 4) === '!--') {
+                    ++index; // `<`
+                    ++index; // `!`
+                    ++index; // `-`
+                    ++index; // `-`
+                    skipSingleLineComment(4);
+                } else {
+                    break;
+                }
+            } else {
+                break;
+            }
+        }
+    }
+
+    function scanHexEscape(prefix) {
+        var i, len, ch, code = 0;
+
+        len = (prefix === 'u') ? 4 : 2;
+        for (i = 0; i < len; ++i) {
+            if (index < length && isHexDigit(source[index])) {
+                ch = source[index++];
+                code = code * 16 + '0123456789abcdef'.indexOf(ch.toLowerCase());
+            } else {
+                return '';
+            }
+        }
+        return String.fromCharCode(code);
+    }
+
+    function getEscapedIdentifier() {
+        var ch, id;
+
+        ch = source.charCodeAt(index++);
+        id = String.fromCharCode(ch);
+
+        // '\u' (U+005C, U+0075) denotes an escaped character.
+        if (ch === 0x5C) {
+            if (source.charCodeAt(index) !== 0x75) {
+                throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
+            }
+            ++index;
+            ch = scanHexEscape('u');
+            if (!ch || ch === '\\' || !isIdentifierStart(ch.charCodeAt(0))) {
+                throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
+            }
+            id = ch;
+        }
+
+        while (index < length) {
+            ch = source.charCodeAt(index);
+            if (!isIdentifierPart(ch)) {
+                break;
+            }
+            ++index;
+            id += String.fromCharCode(ch);
+
+            // '\u' (U+005C, U+0075) denotes an escaped character.
+            if (ch === 0x5C) {
+                id = id.substr(0, id.length - 1);
+                if (source.charCodeAt(index) !== 0x75) {
+                    throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
+                }
+                ++index;
+                ch = scanHexEscape('u');
+                if (!ch || ch === '\\' || !isIdentifierPart(ch.charCodeAt(0))) {
+                    throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
+                }
+                id += ch;
+            }
+        }
+
+        return id;
+    }
+
+    function getIdentifier() {
+        var start, ch;
+
+        start = index++;
+        while (index < length) {
+            ch = source.charCodeAt(index);
+            if (ch === 0x5C) {
+                // Blackslash (U+005C) marks Unicode escape sequence.
+                index = start;
+                return getEscapedIdentifier();
+            }
+            if (isIdentifierPart(ch)) {
+                ++index;
+            } else {
+                break;
+            }
+        }
+
+        return source.slice(start, index);
+    }
+
+    function scanIdentifier() {
+        var start, id, type;
+
+        start = index;
+
+        // Backslash (U+005C) starts an escaped character.
+        id = (source.charCodeAt(index) === 0x5C) ? getEscapedIdentifier() : getIdentifier();
+
+        // There is no keyword or literal with only one character.
+        // Thus, it must be an identifier.
+        if (id.length === 1) {
+            type = Token.Identifier;
+        } else if (isKeyword(id)) {
+            type = Token.Keyword;
+        } else if (id === 'null') {
+            type = Token.NullLiteral;
+        } else if (id === 'true' || id === 'false') {
+            type = Token.BooleanLiteral;
+        } else {
+            type = Token.Identifier;
+        }
+
+        return {
+            type: type,
+            value: id,
+            lineNumber: lineNumber,
+            lineStart: lineStart,
+            start: start,
+            end: index
+        };
+    }
+
+
+    // 7.7 Punctuators
+
+    function scanPunctuator() {
+        var start = index,
+            code = source.charCodeAt(index),
+            code2,
+            ch1 = source[index],
+            ch2,
+            ch3,
+            ch4;
+
+        switch (code) {
+
+        // Check for most common single-character punctuators.
+        case 0x2E:  // . dot
+        case 0x28:  // ( open bracket
+        case 0x29:  // ) close bracket
+        case 0x3B:  // ; semicolon
+        case 0x2C:  // , comma
+        case 0x7B:  // { open curly brace
+        case 0x7D:  // } close curly brace
+        case 0x5B:  // [
+        case 0x5D:  // ]
+        case 0x3A:  // :
+        case 0x3F:  // ?
+        case 0x7E:  // ~
+            ++index;
+            if (extra.tokenize) {
+                if (code === 0x28) {
+                    extra.openParenToken = extra.tokens.length;
+                } else if (code === 0x7B) {
+                    extra.openCurlyToken = extra.tokens.length;
+                }
+            }
+            return {
+                type: Token.Punctuator,
+                value: String.fromCharCode(code),
+                lineNumber: lineNumber,
+                lineStart: lineStart,
+                start: start,
+                end: index
+            };
+
+        default:
+            code2 = source.charCodeAt(index + 1);
+
+            // '=' (U+003D) marks an assignment or comparison operator.
+            if (code2 === 0x3D) {
+                switch (code) {
+                case 0x2B:  // +
+                case 0x2D:  // -
+                case 0x2F:  // /
+                case 0x3C:  // <
+                case 0x3E:  // >
+                case 0x5E:  // ^
+                case 0x7C:  // |
+                case 0x25:  // %
+                case 0x26:  // &
+                case 0x2A:  // *
+                    index += 2;
+                    return {
+                        type: Token.Punctuator,
+                        value: String.fromCharCode(code) + String.fromCharCode(code2),
+                        lineNumber: lineNumber,
+                        lineStart: lineStart,
+                        start: start,
+                        end: index
+                    };
+
+                case 0x21: // !
+                case 0x3D: // =
+                    index += 2;
+
+                    // !== and ===
+                    if (source.charCodeAt(index) === 0x3D) {
+                        ++index;
+                    }
+                    return {
+                        type: Token.Punctuator,
+                        value: source.slice(start, index),
+                        lineNumber: lineNumber,
+                        lineStart: lineStart,
+                        start: start,
+                        end: index
+                    };
+                }
+            }
+        }
+
+        // 4-character punctuator: >>>=
+
+        ch4 = source.substr(index, 4);
+
+        if (ch4 === '>>>=') {
+            index += 4;
+            return {
+                type: Token.Punctuator,
+                value: ch4,
+                lineNumber: lineNumber,
+                lineStart: lineStart,
+                start: start,
+                end: index
+            };
+        }
+
+        // 3-character punctuators: === !== >>> <<= >>=
+
+        ch3 = ch4.substr(0, 3);
+
+        if (ch3 === '>>>' || ch3 === '<<=' || ch3 === '>>=') {
+            index += 3;
+            return {
+                type: Token.Punctuator,
+                value: ch3,
+                lineNumber: lineNumber,
+                lineStart: lineStart,
+                start: start,
+                end: index
+            };
+        }
+
+        // Other 2-character punctuators: ++ -- << >> && ||
+        ch2 = ch3.substr(0, 2);
+
+        if ((ch1 === ch2[1] && ('+-<>&|'.indexOf(ch1) >= 0)) || ch2 === '=>') {
+            index += 2;
+            return {
+                type: Token.Punctuator,
+                value: ch2,
+                lineNumber: lineNumber,
+                lineStart: lineStart,
+                start: start,
+                end: index
+            };
+        }
+
+        // 1-character punctuators: < > = ! + - * % & | ^ /
+        if ('<>=!+-*%&|^/'.indexOf(ch1) >= 0) {
+            ++index;
+            return {
+                type: Token.Punctuator,
+                value: ch1,
+                lineNumber: lineNumber,
+                lineStart: lineStart,
+                start: start,
+                end: index
+            };
+        }
+
+        throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
+    }
+
+    // 7.8.3 Numeric Literals
+
+    function scanHexLiteral(start) {
+        var number = '';
+
+        while (index < length) {
+            if (!isHexDigit(source[index])) {
+                break;
+            }
+            number += source[index++];
+        }
+
+        if (number.length === 0) {
+            throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
+        }
+
+        if (isIdentifierStart(source.charCodeAt(index))) {
+            throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
+        }
+
+        return {
+            type: Token.NumericLiteral,
+            value: parseInt('0x' + number, 16),
+            lineNumber: lineNumber,
+            lineStart: lineStart,
+            start: start,
+            end: index
+        };
+    }
+
+    function scanOctalLiteral(start) {
+        var number = '0' + source[index++];
+        while (index < length) {
+            if (!isOctalDigit(source[index])) {
+                break;
+            }
+            number += source[index++];
+        }
+
+        if (isIdentifierStart(source.charCodeAt(index)) || isDecimalDigit(source.charCodeAt(index))) {
+            throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
+        }
+
+        return {
+            type: Token.NumericLiteral,
+            value: parseInt(number, 8),
+            octal: true,
+            lineNumber: lineNumber,
+            lineStart: lineStart,
+            start: start,
+            end: index
+        };
+    }
+
+    function scanNumericLiteral() {
+        var number, start, ch;
+
+        ch = source[index];
+        assert(isDecimalDigit(ch.charCodeAt(0)) || (ch === '.'),
+            'Numeric literal must start with a decimal digit or a decimal point');
+
+        start = index;
+        number = '';
+        if (ch !== '.') {
+            number = source[index++];
+            ch = source[index];
+
+            // Hex number starts with '0x'.
+            // Octal number starts with '0'.
+            if (number === '0') {
+                if (ch === 'x' || ch === 'X') {
+                    ++index;
+                    return scanHexLiteral(start);
+                }
+                if (isOctalDigit(ch)) {
+                    return scanOctalLiteral(start);
+                }
+
+                // decimal number starts with '0' such as '09' is illegal.
+                if (ch && isDecimalDigit(ch.charCodeAt(0))) {
+                    throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
+                }
+            }
+
+            while (isDecimalDigit(source.charCodeAt(index))) {
+                number += source[index++];
+            }
+            ch = source[index];
+        }
+
+        if (ch === '.') {
+            number += source[index++];
+            while (isDecimalDigit(source.charCodeAt(index))) {
+                number += source[index++];
+            }
+            ch = source[index];
+        }
+
+        if (ch === 'e' || ch === 'E') {
+            number += source[index++];
+
+            ch = source[index];
+            if (ch === '+' || ch === '-') {
+                number += source[index++];
+            }
+            if (isDecimalDigit(source.charCodeAt(index))) {
+                while (isDecimalDigit(source.charCodeAt(index))) {
+                    number += source[index++];
+                }
+            } else {
+                throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
+            }
+        }
+
+        if (isIdentifierStart(source.charCodeAt(index))) {
+            throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
+        }
+
+        return {
+            type: Token.NumericLiteral,
+            value: parseFloat(number),
+            lineNumber: lineNumber,
+            lineStart: lineStart,
+            start: start,
+            end: index
+        };
+    }
+
+    // 7.8.4 String Literals
+
+    function scanStringLiteral() {
+        var str = '', quote, start, ch, code, unescaped, restore, octal = false, startLineNumber, startLineStart;
+        startLineNumber = lineNumber;
+        startLineStart = lineStart;
+
+        quote = source[index];
+        assert((quote === '\'' || quote === '"'),
+            'String literal must starts with a quote');
+
+        start = index;
+        ++index;
+
+        while (index < length) {
+            ch = source[index++];
+
+            if (ch === quote) {
+                quote = '';
+                break;
+            } else if (ch === '\\') {
+                ch = source[index++];
+                if (!ch || !isLineTerminator(ch.charCodeAt(0))) {
+                    switch (ch) {
+                    case 'u':
+                    case 'x':
+                        restore = index;
+                        unescaped = scanHexEscape(ch);
+                        if (unescaped) {
+                            str += unescaped;
+                        } else {
+                            index = restore;
+                            str += ch;
+                        }
+                        break;
+                    case 'n':
+                        str += '\n';
+                        break;
+                    case 'r':
+                        str += '\r';
+                        break;
+                    case 't':
+                        str += '\t';
+                        break;
+                    case 'b':
+                        str += '\b';
+                        break;
+                    case 'f':
+                        str += '\f';
+                        break;
+                    case 'v':
+                        str += '\x0B';
+                        break;
+
+                    default:
+                        if (isOctalDigit(ch)) {
+                            code = '01234567'.indexOf(ch);
+
+                            // \0 is not octal escape sequence
+                            if (code !== 0) {
+                                octal = true;
+                            }
+
+                            if (index < length && isOctalDigit(source[index])) {
+                                octal = true;
+                                code = code * 8 + '01234567'.indexOf(source[index++]);
+
+                                // 3 digits are only allowed when string starts
+                                // with 0, 1, 2, 3
+                                if ('0123'.indexOf(ch) >= 0 &&
+                                        index < length &&
+                                        isOctalDigit(source[index])) {
+                                    code = code * 8 + '01234567'.indexOf(source[index++]);
+                                }
+                            }
+                            str += String.fromCharCode(code);
+                        } else {
+                            str += ch;
+                        }
+                        break;
+                    }
+                } else {
+                    ++lineNumber;
+                    if (ch ===  '\r' && source[index] === '\n') {
+                        ++index;
+                    }
+                    lineStart = index;
+                }
+            } else if (isLineTerminator(ch.charCodeAt(0))) {
+                break;
+            } else {
+                str += ch;
+            }
+        }
+
+        if (quote !== '') {
+            throwError({}, Messages.UnexpectedToken, 'ILLEGAL');
+        }
+
+        return {
+            type: Token.StringLiteral,
+            value: str,
+            octal: octal,
+            startLineNumber: startLineNumber,
+            startLineStart: startLineStart,
+            lineNumber: lineNumber,
+            lineStart: lineStart,
+            start: start,
+            end: index
+        };
+    }
+
+    function testRegExp(pattern, flags) {
+        var value;
+        try {
+            value = new RegExp(pattern, flags);
+        } catch (e) {
+            throwError({}, Messages.InvalidRegExp);
+        }
+        return value;
+    }
+
+    function scanRegExpBody() {
+        var ch, str, classMarker, terminated, body;
+
+        ch = source[index];
+        assert(ch === '/', 'Regular expression literal must start with a slash');
+        str = source[index++];
+
+        classMarker = false;
+        terminated = false;
+        while (index < length) {
+            ch = source[index++];
+            str += ch;
+            if (ch === '\\') {
+                ch = source[index++];
+                // ECMA-262 7.8.5
+                if (isLineTerminator(ch.charCodeAt(0))) {
+                    throwError({}, Messages.UnterminatedRegExp);
+                }
+                str += ch;
+            } else if (isLineTerminator(ch.charCodeAt(0))) {
+                throwError({}, Messages.UnterminatedRegExp);
+            } else if (classMarker) {
+                if (ch === ']') {
+                    classMarker = false;
+                }
+            } else {
+                if (ch === '/') {
+                    terminated = true;
+                    break;
+                } else if (ch === '[') {
+                    classMarker = true;
+                }
+            }
+        }
+
+        if (!terminated) {
+            throwError({}, Messages.UnterminatedRegExp);
+        }
+
+        // Exclude leading and trailing slash.
+        body = str.substr(1, str.length - 2);
+        return {
+            value: body,
+            literal: str
+        };
+    }
+
+    function scanRegExpFlags() {
+        var ch, str, flags, restore;
+
+        str = '';
+        flags = '';
+        while (index < length) {
+            ch = source[index];
+            if (!isIdentifierPart(ch.charCodeAt(0))) {
+                break;
+            }
+
+            ++index;
+            if (ch === '\\' && index < length) {
+                ch = source[index];
+                if (ch === 'u') {
+                    ++index;
+                    restore = index;
+                    ch = scanHexEscape('u');
+                    if (ch) {
+                        flags += ch;
+                        for (str += '\\u'; restore < index; ++restore) {
+                            str += source[restore];
+                        }
+                    } else {
+                        index = restore;
+                        flags += 'u';
+                        str += '\\u';
+                    }
+                    throwErrorTolerant({}, Messages.UnexpectedToken, 'ILLEGAL');
+                } else {
+                    str += '\\';
+                    throwErrorTolerant({}, Messages.UnexpectedToken, 'ILLEGAL');
+                }
+            } else {
+                flags += ch;
+                str += ch;
+            }
+        }
+
+        return {
+            value: flags,
+            literal: str
+        };
+    }
+
+    function scanRegExp() {
+        var start, body, flags, pattern, value;
+
+        lookahead = null;
+        skipComment();
+        start = index;
+
+        body = scanRegExpBody();
+        flags = scanRegExpFlags();
+        value = testRegExp(body.value, flags.value);
+
+        if (extra.tokenize) {
+            return {
+                type: Token.RegularExpression,
+                value: value,
+                lineNumber: lineNumber,
+                lineStart: lineStart,
+                start: start,
+                end: index
+            };
+        }
+
+        return {
+            literal: body.literal + flags.literal,
+            value: value,
+            start: start,
+            end: index
+        };
+    }
+
+    function collectRegex() {
+        var pos, loc, regex, token;
+
+        skipComment();
+
+        pos = index;
+        loc = {
+            start: {
+                line: lineNumber,
+                column: index - lineStart
+            }
+        };
+
+        regex = scanRegExp();
+        loc.end = {
+            line: lineNumber,
+            column: index - lineStart
+        };
+
+        /* istanbul ignore next */
+        if (!extra.tokenize) {
+            // Pop the previous token, which is likely '/' or '/='
+            if (extra.tokens.length > 0) {
+                token = extra.tokens[extra.tokens.length - 1];
+                if (token.range[0] === pos && token.type === 'Punctuator') {
+                    if (token.value === '/' || token.value === '/=') {
+                        extra.tokens.pop();
+                    }
+                }
+            }
+
+            extra.tokens.push({
+                type: 'RegularExpression',
+                value: regex.literal,
+                range: [pos, index],
+                loc: loc
+            });
+        }
+
+        return regex;
+    }
+
+    function isIdentifierName(token) {
+        return token.type === Token.Identifier ||
+            token.type === Token.Keyword ||
+            token.type === Token.BooleanLiteral ||
+            token.type === Token.NullLiteral;
+    }
+
+    function advanceSlash() {
+        var prevToken,
+            checkToken;
+        // Using the following algorithm:
+        // https://github.com/mozilla/sweet.js/wiki/design
+        prevToken = extra.tokens[extra.tokens.length - 1];
+        if (!prevToken) {
+            // Nothing before that: it cannot be a division.
+            return collectRegex();
+        }
+        if (prevToken.type === 'Punctuator') {
+            if (prevToken.value === ']') {
+                return scanPunctuator();
+            }
+            if (prevToken.value === ')') {
+                checkToken = extra.tokens[extra.openParenToken - 1];
+                if (checkToken &&
+                        checkToken.type === 'Keyword' &&
+                        (checkToken.value === 'if' ||
+                         checkToken.value === 'while' ||
+                         checkToken.value === 'for' ||
+                         checkToken.value === 'with')) {
+                    return collectRegex();
+                }
+                return scanPunctuator();
+            }
+            if (prevToken.value === '}') {
+                // Dividing a function by anything makes little sense,
+                // but we have to check for that.
+                if (extra.tokens[extra.openCurlyToken - 3] &&
+                        extra.tokens[extra.openCurlyToken - 3].type === 'Keyword') {
+                    // Anonymous function.
+                    checkToken = extra.tokens[extra.openCurlyToken - 4];
+                    if (!checkToken) {
+                        return scanPunctuator();
+                    }
+                } else if (extra.tokens[extra.openCurlyToken - 4] &&
+                        extra.tokens[extra.openCurlyToken - 4].type === 'Keyword') {
+                    // Named function.
+                    checkToken = extra.tokens[extra.openCurlyToken - 5];
+                    if (!checkToken) {
+                        return collectRegex();
+                    }
+                } else {
+                    return scanPunctuator();
+                }
+                // checkToken determines whether the function is
+                // a declaration or an expression.
+                if (FnExprTokens.indexOf(checkToken.value) >= 0) {
+                    // It is an expression.
+                    return scanPunctuator();
+                }
+                // It is a declaration.
+                return collectRegex();
+            }
+            return collectRegex();
+        }
+        if (prevToken.type === 'Keyword') {
+            return collectRegex();
+        }
+        return scanPunctuator();
+    }
+
+    function advance() {
+        var ch;
+
+        skipComment();
+
+        if (index >= length) {
+            return {
+                type: Token.EOF,
+                lineNumber: lineNumber,
+                lineStart: lineStart,
+                start: index,
+                end: index
+            };
+        }
+
+        ch = source.charCodeAt(index);
+
+        if (isIdentifierStart(ch)) {
+            return scanIdentifier();
+        }
+
+        // Very common: ( and ) and ;
+        if (ch === 0x28 || ch === 0x29 || ch === 0x3B) {
+            return scanPunctuator();
+        }
+
+        // String literal starts with single quote (U+0027) or double quote (U+0022).
+        if (ch === 0x27 || ch === 0x22) {
+            return scanStringLiteral();
+        }
+
+
+        // Dot (.) U+002E can also start a floating-point number, hence the need
+        // to check the next character.
+        if (ch === 0x2E) {
+            if (isDecimalDigit(source.charCodeAt(index + 1))) {
+                return scanNumericLiteral();
+            }
+            return scanPunctuator();
+        }
+
+        if (isDecimalDigit(ch)) {
+            return scanNumericLiteral();
+        }
+
+        // Slash (/) U+002F can also start a regex.
+        if (extra.tokenize && ch === 0x2F) {
+            return advanceSlash();
+        }
+
+        return scanPunctuator();
+    }
+
+    function collectToken() {
+        var loc, token, range, value;
+
+        skipComment();
+        loc = {
+            start: {
+                line: lineNumber,
+                column: index - lineStart
+            }
+        };
+
+        token = advance();
+        loc.end = {
+            line: lineNumber,
+            column: index - lineStart
+        };
+
+        if (token.type !== Token.EOF) {
+            value = source.slice(token.start, token.end);
+            extra.tokens.push({
+                type: TokenName[token.type],
+                value: value,
+                range: [token.start, token.end],
+                loc: loc
+            });
+        }
+
+        return token;
+    }
+
+    function lex() {
+        var token;
+
+        token = lookahead;
+        index = token.end;
+        lineNumber = token.lineNumber;
+        lineStart = token.lineStart;
+
+        lookahead = (typeof extra.tokens !== 'undefined') ? collectToken() : advance();
+
+        index = token.end;
+        lineNumber = token.lineNumber;
+        lineStart = token.lineStart;
+
+        return token;
+    }
+
+    function peek() {
+        var pos, line, start;
+
+        pos = index;
+        line = lineNumber;
+        start = lineStart;
+        lookahead = (typeof extra.tokens !== 'undefined') ? collectToken() : advance();
+        index = pos;
+        lineNumber = line;
+        lineStart = start;
+    }
+
+    function Position(line, column) {
+        this.line = line;
+        this.column = column;
+    }
+
+    function SourceLocation(startLine, startColumn, line, column) {
+        this.start = new Position(startLine, startColumn);
+        this.end = new Position(line, column);
+    }
+
+    SyntaxTreeDelegate = {
+
+        name: 'SyntaxTree',
+
+        processComment: function (node) {
+            var lastChild, trailingComments;
+
+            if (node.type === Syntax.Program) {
+                if (node.body.length > 0) {
+                    return;
+                }
+            }
+
+            if (extra.trailingComments.length > 0) {
+                if (extra.trailingComments[0].range[0] >= node.range[1]) {
+                    trailingComments = extra.trailingComments;
+                    extra.trailingComments = [];
+                } else {
+                    extra.trailingComments.length = 0;
+                }
+            } else {
+                if (extra.bottomRightStack.length > 0 &&
+                        extra.bottomRightStack[extra.bottomRightStack.length - 1].trailingComments &&
+                        extra.bottomRightStack[extra.bottomRightStack.length - 1].trailingComments[0].range[0] >= node.range[1]) {
+                    trailingComments = extra.bottomRightStack[extra.bottomRightStack.length - 1].trailingComments;
+                    delete extra.bottomRightStack[extra.bottomRightStack.length - 1].trailingComments;
+                }
+            }
+
+            // Eating the stack.
+            while (extra.bottomRightStack.length > 0 && extra.bottomRightStack[extra.bottomRightStack.length - 1].range[0] >= node.range[0]) {
+                lastChild = extra.bottomRightStack.pop();
+            }
+
+            if (lastChild) {
+                if (lastChild.leadingComments && lastChild.leadingComments[lastChild.leadingComments.length - 1].range[1] <= node.range[0]) {
+                    node.leadingComments = lastChild.leadingComments;
+                    delete lastChild.leadingComments;
+                }
+            } else if (extra.leadingComments.length > 0 && extra.leadingComments[extra.leadingComments.length - 1].range[1] <= node.range[0]) {
+                node.leadingComments = extra.leadingComments;
+                extra.leadingComments = [];
+            }
+
+
+            if (trailingComments) {
+                node.trailingComments = trailingComments;
+            }
+
+            extra.bottomRightStack.push(node);
+        },
+
+        markEnd: function (node, startToken) {
+            if (extra.range) {
+                node.range = [startToken.start, index];
+            }
+            if (extra.loc) {
+                node.loc = new SourceLocation(
+                    startToken.startLineNumber === undefined ?  startToken.lineNumber : startToken.startLineNumber,
+                    startToken.start - (startToken.startLineStart === undefined ?  startToken.lineStart : startToken.startLineStart),
+                    lineNumber,
+                    index - lineStart
+                );
+                this.postProcess(node);
+            }
+
+            if (extra.attachComment) {
+                this.processComment(node);
+            }
+            return node;
+        },
+
+        postProcess: function (node) {
+            if (extra.source) {
+                node.loc.source = extra.source;
+            }
+            return node;
+        },
+
+        createArrayExpression: function (elements) {
+            return {
+                type: Syntax.ArrayExpression,
+                elements: elements
+            };
+        },
+
+        createAssignmentExpression: function (operator, left, right) {
+            return {
+                type: Syntax.AssignmentExpression,
+                operator: operator,
+                left: left,
+                right: right
+            };
+        },
+
+        createBinaryExpression: function (operator, left, right) {
+            var type = (operator === '||' || operator === '&&') ? Syntax.LogicalExpression :
+                        Syntax.BinaryExpression;
+            return {
+                type: type,
+                operator: operator,
+                left: left,
+                right: right
+            };
+        },
+
+        createBlockStatement: function (body) {
+            return {
+                type: Syntax.BlockStatement,
+                body: body
+            };
+        },
+
+        createBreakStatement: function (label) {
+            return {
+                type: Syntax.BreakStatement,
+                label: label
+            };
+        },
+
+        createCallExpression: function (callee, args) {
+            return {
+                type: Syntax.CallExpression,
+                callee: callee,
+                'arguments': args
+            };
+        },
+
+        createCatchClause: function (param, body) {
+            return {
+                type: Syntax.CatchClause,
+                param: param,
+                body: body
+            };
+        },
+
+        createConditionalExpression: function (test, consequent, alternate) {
+            return {
+                type: Syntax.ConditionalExpression,
+                test: test,
+                consequent: consequent,
+                alternate: alternate
+            };
+        },
+
+        createContinueStatement: function (label) {
+            return {
+                type: Syntax.ContinueStatement,
+                label: label
+            };
+        },
+
+        createDebuggerStatement: function () {
+            return {
+                type: Syntax.DebuggerStatement
+            };
+        },
+
+        createDoWhileStatement: function (body, test) {
+            return {
+                type: Syntax.DoWhileStatement,
+                body: body,
+                test: test
+            };
+        },
+
+        createEmptyStatement: function () {
+            return {
+                type: Syntax.EmptyStatement
+            };
+        },
+
+        createExpressionStatement: function (expression) {
+            return {
+                type: Syntax.ExpressionStatement,
+                expression: expression
+            };
+        },
+
+        createForStatement: function (init, test, update, body) {
+            return {
+                type: Syntax.ForStatement,
+                init: init,
+                test: test,
+                update: update,
+                body: body
+            };
+        },
+
+        createForInStatement: function (left, right, body) {
+            return {
+                type: Syntax.ForInStatement,
+                left: left,
+                right: right,
+                body: body,
+                each: false
+            };
+        },
+
+        createFunctionDeclaration: function (id, params, defaults, body) {
+            return {
+                type: Syntax.FunctionDeclaration,
+                id: id,
+                params: params,
+                defaults: defaults,
+                body: body,
+                rest: null,
+                generator: false,
+                expression: false
+            };
+        },
+
+        createFunctionExpression: function (id, params, defaults, body) {
+            return {
+                type: Syntax.FunctionExpression,
+                id: id,
+                params: params,
+                defaults: defaults,
+                body: body,
+                rest: null,
+                generator: false,
+                expression: false
+            };
+        },
+
+        createIdentifier: function (name) {
+            return {
+                type: Syntax.Identifier,
+                name: name
+            };
+        },
+
+        createIfStatement: function (test, consequent, alternate) {
+            return {
+                type: Syntax.IfStatement,
+                test: test,
+                consequent: consequent,
+                alternate: alternate
+            };
+        },
+
+        createLabeledStatement: function (label, body) {
+            return {
+                type: Syntax.LabeledStatement,
+                label: label,
+                body: body
+            };
+        },
+
+        createLiteral: function (token) {
+            return {
+                type: Syntax.Literal,
+                value: token.value,
+                raw: source.slice(token.start, token.end)
+            };
+        },
+
+        createMemberExpression: function (accessor, object, property) {
+            return {
+                type: Syntax.MemberExpression,
+                computed: accessor === '[',
+                object: object,
+                property: property
+            };
+        },
+
+        createNewExpression: function (callee, args) {
+            return {
+                type: Syntax.NewExpression,
+                callee: callee,
+                'arguments': args
+            };
+        },
+
+        createObjectExpression: function (properties) {
+            return {
+                type: Syntax.ObjectExpression,
+                properties: properties
+            };
+        },
+
+        createPostfixExpression: function (operator, argument) {
+            return {
+                type: Syntax.UpdateExpression,
+                operator: operator,
+                argument: argument,
+                prefix: false
+            };
+        },
+
+        createProgram: function (body) {
+            return {
+                type: Syntax.Program,
+                body: body
+            };
+        },
+
+        createProperty: function (kind, key, value) {
+            return {
+                type: Syntax.Property,
+                key: key,
+                value: value,
+                kind: kind
+            };
+        },
+
+        createReturnStatement: function (argument) {
+            return {
+                type: Syntax.ReturnStatement,
+                argument: argument
+            };
+        },
+
+        createSequenceExpression: function (expressions) {
+            return {
+                type: Syntax.SequenceExpression,
+                expressions: expressions
+            };
+        },
+
+        createSwitchCase: function (test, consequent) {
+            return {
+                type: Syntax.SwitchCase,
+                test: test,
+                consequent: consequent
+            };
+        },
+
+        createSwitchStatement: function (discriminant, cases) {
+            return {
+                type: Syntax.SwitchStatement,
+                discriminant: discriminant,
+                cases: cases
+            };
+        },
+
+        createThisExpression: function () {
+            return {
+                type: Syntax.ThisExpression
+            };
+        },
+
+        createThrowStatement: function (argument) {
+            return {
+                type: Syntax.ThrowStatement,
+                argument: argument
+            };
+        },
+
+        createTryStatement: function (block, guardedHandlers, handlers, finalizer) {
+            return {
+                type: Syntax.TryStatement,
+                block: block,
+                guardedHandlers: guardedHandlers,
+                handlers: handlers,
+                finalizer: finalizer
+            };
+        },
+
+        createUnaryExpression: function (operator, argument) {
+            if (operator === '++' || operator === '--') {
+                return {
+                    type: Syntax.UpdateExpression,
+                    operator: operator,
+                    argument: argument,
+                    prefix: true
+                };
+            }
+            return {
+                type: Syntax.UnaryExpression,
+                operator: operator,
+                argument: argument,
+                prefix: true
+            };
+        },
+
+        createVariableDeclaration: function (declarations, kind) {
+            return {
+                type: Syntax.VariableDeclaration,
+                declarations: declarations,
+                kind: kind
+            };
+        },
+
+        createVariableDeclarator: function (id, init) {
+            return {
+                type: Syntax.VariableDeclarator,
+                id: id,
+                init: init
+            };
+        },
+
+        createWhileStatement: function (test, body) {
+            return {
+                type: Syntax.WhileStatement,
+                test: test,
+                body: body
+            };
+        },
+
+        createWithStatement: function (object, body) {
+            return {
+                type: Syntax.WithStatement,
+                object: object,
+                body: body
+            };
+        }
+    };
+
+    // Return true if there is a line terminator before the next token.
+
+    function peekLineTerminator() {
+        var pos, line, start, found;
+
+        pos = index;
+        line = lineNumber;
+        start = lineStart;
+        skipComment();
+        found = lineNumber !== line;
+        index = pos;
+        lineNumber = line;
+        lineStart = start;
+
+        return found;
+    }
+
+    // Throw an exception
+
+    function throwError(token, messageFormat) {
+        var error,
+            args = Array.prototype.slice.call(arguments, 2),
+            msg = messageFormat.replace(
+                /%(\d)/g,
+                function (whole, index) {
+                    assert(index < args.length, 'Message reference must be in range');
+                    return args[index];
+                }
+            );
+
+        if (typeof token.lineNumber === 'number') {
+            error = new Error('Line ' + token.lineNumber + ': ' + msg);
+            error.index = token.start;
+            error.lineNumber = token.lineNumber;
+            error.column = token.start - lineStart + 1;
+        } else {
+            error = new Error('Line ' + lineNumber + ': ' + msg);
+            error.index = index;
+            error.lineNumber = lineNumber;
+            error.column = index - lineStart + 1;
+        }
+
+        error.description = msg;
+        throw error;
+    }
+
+    function throwErrorTolerant() {
+        try {
+            throwError.apply(null, arguments);
+        } catch (e) {
+            if (extra.errors) {
+                extra.errors.push(e);
+            } else {
+                throw e;
+            }
+        }
+    }
+
+
+    // Throw an exception because of the token.
+
+    function throwUnexpected(token) {
+        if (token.type === Token.EOF) {
+            throwError(token, Messages.UnexpectedEOS);
+        }
+
+        if (token.type === Token.NumericLiteral) {
+            throwError(token, Messages.UnexpectedNumber);
+        }
+
+        if (token.type === Token.StringLiteral) {
+            throwError(token, Messages.UnexpectedString);
+        }
+
+        if (token.type === Token.Identifier) {
+            throwError(token, Messages.UnexpectedIdentifier);
+        }
+
+        if (token.type === Token.Keyword) {
+            if (isFutureReservedWord(token.value)) {
+                throwError(token, Messages.UnexpectedReserved);
+            } else if (strict && isStrictModeReservedWord(token.value)) {
+                throwErrorTolerant(token, Messages.StrictReservedWord);
+                return;
+            }
+            throwError(token, Messages.UnexpectedToken, token.value);
+        }
+
+        // BooleanLiteral, NullLiteral, or Punctuator.
+        throwError(token, Messages.UnexpectedToken, token.value);
+    }
+
+    // Expect the next token to match the specified punctuator.
+    // If not, an exception will be thrown.
+
+    function expect(value) {
+        var token = lex();
+        if (token.type !== Token.Punctuator || token.value !== value) {
+            throwUnexpected(token);
+        }
+    }
+
+    // Expect the next token to match the specified keyword.
+    // If not, an exception will be thrown.
+
+    function expectKeyword(keyword) {
+        var token = lex();
+        if (token.type !== Token.Keyword || token.value !== keyword) {
+            throwUnexpected(token);
+        }
+    }
+
+    // Return true if the next token matches the specified punctuator.
+
+    function match(value) {
+        return lookahead.type === Token.Punctuator && lookahead.value === value;
+    }
+
+    // Return true if the next token matches the specified keyword
+
+    function matchKeyword(keyword) {
+        return lookahead.type === Token.Keyword && lookahead.value === keyword;
+    }
+
+    // Return true if the next token is an assignment operator
+
+    function matchAssign() {
+        var op;
+
+        if (lookahead.type !== Token.Punctuator) {
+            return false;
+        }
+        op = lookahead.value;
+        return op === '=' ||
+            op === '*=' ||
+            op === '/=' ||
+            op === '%=' ||
+            op === '+=' ||
+            op === '-=' ||
+            op === '<<=' ||
+            op === '>>=' ||
+            op === '>>>=' ||
+            op === '&=' ||
+            op === '^=' ||
+            op === '|=';
+    }
+
+    function consumeSemicolon() {
+        var line;
+
+        // Catch the very common case first: immediately a semicolon (U+003B).
+        if (source.charCodeAt(index) === 0x3B || match(';')) {
+            lex();
+            return;
+        }
+
+        line = lineNumber;
+        skipComment();
+        if (lineNumber !== line) {
+            return;
+        }
+
+        if (lookahead.type !== Token.EOF && !match('}')) {
+            throwUnexpected(lookahead);
+        }
+    }
+
+    // Return true if provided expression is LeftHandSideExpression
+
+    function isLeftHandSide(expr) {
+        return expr.type === Syntax.Identifier || expr.type === Syntax.MemberExpression;
+    }
+
+    // 11.1.4 Array Initialiser
+
+    function parseArrayInitialiser() {
+        var elements = [], startToken;
+
+        startToken = lookahead;
+        expect('[');
+
+        while (!match(']')) {
+            if (match(',')) {
+                lex();
+                elements.push(null);
+            } else {
+                elements.push(parseAssignmentExpression());
+
+                if (!match(']')) {
+                    expect(',');
+                }
+            }
+        }
+
+        lex();
+
+        return delegate.markEnd(delegate.createArrayExpression(elements), startToken);
+    }
+
+    // 11.1.5 Object Initialiser
+
+    function parsePropertyFunction(param, first) {
+        var previousStrict, body, startToken;
+
+        previousStrict = strict;
+        startToken = lookahead;
+        body = parseFunctionSourceElements();
+        if (first && strict && isRestrictedWord(param[0].name)) {
+            throwErrorTolerant(first, Messages.StrictParamName);
+        }
+        strict = previousStrict;
+        return delegate.markEnd(delegate.createFunctionExpression(null, param, [], body), startToken);
+    }
+
+    function parseObjectPropertyKey() {
+        var token, startToken;
+
+        startToken = lookahead;
+        token = lex();
+
+        // Note: This function is called only from parseObjectProperty(), where
+        // EOF and Punctuator tokens are already filtered out.
+
+        if (token.type === Token.StringLiteral || token.type === Token.NumericLiteral) {
+            if (strict && token.octal) {
+                throwErrorTolerant(token, Messages.StrictOctalLiteral);
+            }
+            return delegate.markEnd(delegate.createLiteral(token), startToken);
+        }
+
+        return delegate.markEnd(delegate.createIdentifier(token.value), startToken);
+    }
+
+    function parseObjectProperty() {
+        var token, key, id, value, param, startToken;
+
+        token = lookahead;
+        startToken = lookahead;
+
+        if (token.type === Token.Identifier) {
+
+            id = parseObjectPropertyKey();
+
+            // Property Assignment: Getter and Setter.
+
+            if (token.value === 'get' && !match(':')) {
+                key = parseObjectPropertyKey();
+                expect('(');
+                expect(')');
+                value = parsePropertyFunction([]);
+                return delegate.markEnd(delegate.createProperty('get', key, value), startToken);
+            }
+            if (token.value === 'set' && !match(':')) {
+                key = parseObjectPropertyKey();
+                expect('(');
+                token = lookahead;
+                if (token.type !== Token.Identifier) {
+                    expect(')');
+                    throwErrorTolerant(token, Messages.UnexpectedToken, token.value);
+                    value = parsePropertyFunction([]);
+                } else {
+                    param = [ parseVariableIdentifier() ];
+                    expect(')');
+                    value = parsePropertyFunction(param, token);
+                }
+                return delegate.markEnd(delegate.createProperty('set', key, value), startToken);
+            }
+            expect(':');
+            value = parseAssignmentExpression();
+            return delegate.markEnd(delegate.createProperty('init', id, value), startToken);
+        }
+        if (token.type === Token.EOF || token.type === Token.Punctuator) {
+            throwUnexpected(token);
+        } else {
+            key = parseObjectPropertyKey();
+            expect(':');
+            value = parseAssignmentExpression();
+            return delegate.markEnd(delegate.createProperty('init', key, value), startToken);
+        }
+    }
+
+    function parseObjectInitialiser() {
+        var properties = [], property, name, key, kind, map = {}, toString = String, startToken;
+
+        startToken = lookahead;
+
+        expect('{');
+
+        while (!match('}')) {
+            property = parseObjectProperty();
+
+            if (property.key.type === Syntax.Identifier) {
+                name = property.key.name;
+            } else {
+                name = toString(property.key.value);
+            }
+            kind = (property.kind === 'init') ? PropertyKind.Data : (property.kind === 'get') ? PropertyKind.Get : PropertyKind.Set;
+
+            key = '$' + name;
+            if (Object.prototype.hasOwnProperty.call(map, key)) {
+                if (map[key] === PropertyKind.Data) {
+                    if (strict && kind === PropertyKind.Data) {
+                        throwErrorTolerant({}, Messages.StrictDuplicateProperty);
+                    } else if (kind !== PropertyKind.Data) {
+                        throwErrorTolerant({}, Messages.AccessorDataProperty);
+                    }
+                } else {
+                    if (kind === PropertyKind.Data) {
+                        throwErrorTolerant({}, Messages.AccessorDataProperty);
+                    } else if (map[key] & kind) {
+                        throwErrorTolerant({}, Messages.AccessorGetSet);
+                    }
+                }
+                map[key] |= kind;
+            } else {
+                map[key] = kind;
+            }
+
+            properties.push(property);
+
+            if (!match('}')) {
+                expect(',');
+            }
+        }
+
+        expect('}');
+
+        return delegate.markEnd(delegate.createObjectExpression(properties), startToken);
+    }
+
+    // 11.1.6 The Grouping Operator
+
+    function parseGroupExpression() {
+        var expr;
+
+        expect('(');
+
+        expr = parseExpression();
+
+        expect(')');
+
+        return expr;
+    }
+
+
+    // 11.1 Primary Expressions
+
+    function parsePrimaryExpression() {
+        var type, token, expr, startToken;
+
+        if (match('(')) {
+            return parseGroupExpression();
+        }
+
+        if (match('[')) {
+            return parseArrayInitialiser();
+        }
+
+        if (match('{')) {
+            return parseObjectInitialiser();
+        }
+
+        type = lookahead.type;
+        startToken = lookahead;
+
+        if (type === Token.Identifier) {
+            expr =  delegate.createIdentifier(lex().value);
+        } else if (type === Token.StringLiteral || type === Token.NumericLiteral) {
+            if (strict && lookahead.octal) {
+                throwErrorTolerant(lookahead, Messages.StrictOctalLiteral);
+            }
+            expr = delegate.createLiteral(lex());
+        } else if (type === Token.Keyword) {
+            if (matchKeyword('function')) {
+                return parseFunctionExpression();
+            }
+            if (matchKeyword('this')) {
+                lex();
+                expr = delegate.createThisExpression();
+            } else {
+                throwUnexpected(lex());
+            }
+        } else if (type === Token.BooleanLiteral) {
+            token = lex();
+            token.value = (token.value === 'true');
+            expr = delegate.createLiteral(token);
+        } else if (type === Token.NullLiteral) {
+            token = lex();
+            token.value = null;
+            expr = delegate.createLiteral(token);
+        } else if (match('/') || match('/=')) {
+            if (typeof extra.tokens !== 'undefined') {
+                expr = delegate.createLiteral(collectRegex());
+            } else {
+                expr = delegate.createLiteral(scanRegExp());
+            }
+            peek();
+        } else {
+            throwUnexpected(lex());
+        }
+
+        return delegate.markEnd(expr, startToken);
+    }
+
+    // 11.2 Left-Hand-Side Expressions
+
+    function parseArguments() {
+        var args = [];
+
+        expect('(');
+
+        if (!match(')')) {
+            while (index < length) {
+                args.push(parseAssignmentExpression());
+                if (match(')')) {
+                    break;
+                }
+                expect(',');
+            }
+        }
+
+        expect(')');
+
+        return args;
+    }
+
+    function parseNonComputedProperty() {
+        var token, startToken;
+
+        startToken = lookahead;
+        token = lex();
+
+        if (!isIdentifierName(token)) {
+            throwUnexpected(token);
+        }
+
+        return delegate.markEnd(delegate.createIdentifier(token.value), startToken);
+    }
+
+    function parseNonComputedMember() {
+        expect('.');
+
+        return parseNonComputedProperty();
+    }
+
+    function parseComputedMember() {
+        var expr;
+
+        expect('[');
+
+        expr = parseExpression();
+
+        expect(']');
+
+        return expr;
+    }
+
+    function parseNewExpression() {
+        var callee, args, startToken;
+
+        startToken = lookahead;
+        expectKeyword('new');
+        callee = parseLeftHandSideExpression();
+        args = match('(') ? parseArguments() : [];
+
+        return delegate.markEnd(delegate.createNewExpression(callee, args), startToken);
+    }
+
+    function parseLeftHandSideExpressionAllowCall() {
+        var previousAllowIn, expr, args, property, startToken;
+
+        startToken = lookahead;
+
+        previousAllowIn = state.allowIn;
+        state.allowIn = true;
+        expr = matchKeyword('new') ? parseNewExpression() : parsePrimaryExpression();
+        state.allowIn = previousAllowIn;
+
+        for (;;) {
+            if (match('.')) {
+                property = parseNonComputedMember();
+                expr = delegate.createMemberExpression('.', expr, property);
+            } else if (match('(')) {
+                args = parseArguments();
+                expr = delegate.createCallExpression(expr, args);
+            } else if (match('[')) {
+                property = parseComputedMember();
+                expr = delegate.createMemberExpression('[', expr, property);
+            } else {
+                break;
+            }
+            delegate.markEnd(expr, startToken);
+        }
+
+        return expr;
+    }
+
+    function parseLeftHandSideExpression() {
+        var previousAllowIn, expr, property, startToken;
+
+        startToken = lookahead;
+
+        previousAllowIn = state.allowIn;
+        expr = matchKeyword('new') ? parseNewExpression() : parsePrimaryExpression();
+        state.allowIn = previousAllowIn;
+
+        while (match('.') || match('[')) {
+            if (match('[')) {
+                property = parseComputedMember();
+                expr = delegate.createMemberExpression('[', expr, property);
+            } else {
+                property = parseNonComputedMember();
+                expr = delegate.createMemberExpression('.', expr, property);
+            }
+            delegate.markEnd(expr, startToken);
+        }
+
+        return expr;
+    }
+
+    // 11.3 Postfix Expressions
+
+    function parsePostfixExpression() {
+        var expr, token, startToken = lookahead;
+
+        expr = parseLeftHandSideExpressionAllowCall();
+
+        if (lookahead.type === Token.Punctuator) {
+            if ((match('++') || match('--')) && !peekLineTerminator()) {
+                // 11.3.1, 11.3.2
+                if (strict && expr.type === Syntax.Identifier && isRestrictedWord(expr.name)) {
+                    throwErrorTolerant({}, Messages.StrictLHSPostfix);
+                }
+
+                if (!isLeftHandSide(expr)) {
+                    throwErrorTolerant({}, Messages.InvalidLHSInAssignment);
+                }
+
+                token = lex();
+                expr = delegate.markEnd(delegate.createPostfixExpression(token.value, expr), startToken);
+            }
+        }
+
+        return expr;
+    }
+
+    // 11.4 Unary Operators
+
+    function parseUnaryExpression() {
+        var token, expr, startToken;
+
+        if (lookahead.type !== Token.Punctuator && lookahead.type !== Token.Keyword) {
+            expr = parsePostfixExpression();
+        } else if (match('++') || match('--')) {
+            startToken = lookahead;
+            token = lex();
+            expr = parseUnaryExpression();
+            // 11.4.4, 11.4.5
+            if (strict && expr.type === Syntax.Identifier && isRestrictedWord(expr.name)) {
+                throwErrorTolerant({}, Messages.StrictLHSPrefix);
+            }
+
+            if (!isLeftHandSide(expr)) {
+                throwErrorTolerant({}, Messages.InvalidLHSInAssignment);
+            }
+
+            expr = delegate.createUnaryExpression(token.value, expr);
+            expr = delegate.markEnd(expr, startToken);
+        } else if (match('+') || match('-') || match('~') || match('!')) {
+            startToken = lookahead;
+            token = lex();
+            expr = parseUnaryExpression();
+            expr = delegate.createUnaryExpression(token.value, expr);
+            expr = delegate.markEnd(expr, startToken);
+        } else if (matchKeyword('delete') || matchKeyword('void') || matchKeyword('typeof')) {
+            startToken = lookahead;
+            token = lex();
+            expr = parseUnaryExpression();
+            expr = delegate.createUnaryExpression(token.value, expr);
+            expr = delegate.markEnd(expr, startToken);
+            if (strict && expr.operator === 'delete' && expr.argument.type === Syntax.Identifier) {
+                throwErrorTolerant({}, Messages.StrictDelete);
+            }
+        } else {
+            expr = parsePostfixExpression();
+        }
+
+        return expr;
+    }
+
+    function binaryPrecedence(token, allowIn) {
+        var prec = 0;
+
+        if (token.type !== Token.Punctuator && token.type !== Token.Keyword) {
+            return 0;
+        }
+
+        switch (token.value) {
+        case '||':
+            prec = 1;
+            break;
+
+        case '&&':
+            prec = 2;
+            break;
+
+        case '|':
+            prec = 3;
+            break;
+
+        case '^':
+            prec = 4;
+            break;
+
+        case '&':
+            prec = 5;
+            break;
+
+        case '==':
+        case '!=':
+        case '===':
+        case '!==':
+            prec = 6;
+            break;
+
+        case '<':
+        case '>':
+        case '<=':
+        case '>=':
+        case 'instanceof':
+            prec = 7;
+            break;
+
+        case 'in':
+            prec = allowIn ? 7 : 0;
+            break;
+
+        case '<<':
+        case '>>':
+        case '>>>':
+            prec = 8;
+            break;
+
+        case '+':
+        case '-':
+            prec = 9;
+            break;
+
+        case '*':
+        case '/':
+        case '%':
+            prec = 11;
+            break;
+
+        default:
+            break;
+        }
+
+        return prec;
+    }
+
+    // 11.5 Multiplicative Operators
+    // 11.6 Additive Operators
+    // 11.7 Bitwise Shift Operators
+    // 11.8 Relational Operators
+    // 11.9 Equality Operators
+    // 11.10 Binary Bitwise Operators
+    // 11.11 Binary Logical Operators
+
+    function parseBinaryExpression() {
+        var marker, markers, expr, token, prec, stack, right, operator, left, i;
+
+        marker = lookahead;
+        left = parseUnaryExpression();
+
+        token = lookahead;
+        prec = binaryPrecedence(token, state.allowIn);
+        if (prec === 0) {
+            return left;
+        }
+        token.prec = prec;
+        lex();
+
+        markers = [marker, lookahead];
+        right = parseUnaryExpression();
+
+        stack = [left, token, right];
+
+        while ((prec = binaryPrecedence(lookahead, state.allowIn)) > 0) {
+
+            // Reduce: make a binary expression from the three topmost entries.
+            while ((stack.length > 2) && (prec <= stack[stack.length - 2].prec)) {
+                right = stack.pop();
+                operator = stack.pop().value;
+                left = stack.pop();
+                expr = delegate.createBinaryExpression(operator, left, right);
+                markers.pop();
+                marker = markers[markers.length - 1];
+                delegate.markEnd(expr, marker);
+                stack.push(expr);
+            }
+
+            // Shift.
+            token = lex();
+            token.prec = prec;
+            stack.push(token);
+            markers.push(lookahead);
+            expr = parseUnaryExpression();
+            stack.push(expr);
+        }
+
+        // Final reduce to clean-up the stack.
+        i = stack.length - 1;
+        expr = stack[i];
+        markers.pop();
+        while (i > 1) {
+            expr = delegate.createBinaryExpression(stack[i - 1].value, stack[i - 2], expr);
+            i -= 2;
+            marker = markers.pop();
+            delegate.markEnd(expr, marker);
+        }
+
+        return expr;
+    }
+
+
+    // 11.12 Conditional Operator
+
+    function parseConditionalExpression() {
+        var expr, previousAllowIn, consequent, alternate, startToken;
+
+        startToken = lookahead;
+
+        expr = parseBinaryExpression();
+
+        if (match('?')) {
+            lex();
+            previousAllowIn = state.allowIn;
+            state.allowIn = true;
+            consequent = parseAssignmentExpression();
+            state.allowIn = previousAllowIn;
+            expect(':');
+            alternate = parseAssignmentExpression();
+
+            expr = delegate.createConditionalExpression(expr, consequent, alternate);
+            delegate.markEnd(expr, startToken);
+        }
+
+        return expr;
+    }
+
+    // 11.13 Assignment Operators
+
+    function parseAssignmentExpression() {
+        var token, left, right, node, startToken;
+
+        token = lookahead;
+        startToken = lookahead;
+
+        node = left = parseConditionalExpression();
+
+        if (matchAssign()) {
+            // LeftHandSideExpression
+            if (!isLeftHandSide(left)) {
+                throwErrorTolerant({}, Messages.InvalidLHSInAssignment);
+            }
+
+            // 11.13.1
+            if (strict && left.type === Syntax.Identifier && isRestrictedWord(left.name)) {
+                throwErrorTolerant(token, Messages.StrictLHSAssignment);
+            }
+
+            token = lex();
+            right = parseAssignmentExpression();
+            node = delegate.markEnd(delegate.createAssignmentExpression(token.value, left, right), startToken);
+        }
+
+        return node;
+    }
+
+    // 11.14 Comma Operator
+
+    function parseExpression() {
+        var expr, startToken = lookahead;
+
+        expr = parseAssignmentExpression();
+
+        if (match(',')) {
+            expr = delegate.createSequenceExpression([ expr ]);
+
+            while (index < length) {
+                if (!match(',')) {
+                    break;
+                }
+                lex();
+                expr.expressions.push(parseAssignmentExpression());
+            }
+
+            delegate.markEnd(expr, startToken);
+        }
+
+        return expr;
+    }
+
+    // 12.1 Block
+
+    function parseStatementList() {
+        var list = [],
+            statement;
+
+        while (index < length) {
+            if (match('}')) {
+                break;
+            }
+            statement = parseSourceElement();
+            if (typeof statement === 'undefined') {
+                break;
+            }
+            list.push(statement);
+        }
+
+        return list;
+    }
+
+    function parseBlock() {
+        var block, startToken;
+
+        startToken = lookahead;
+        expect('{');
+
+        block = parseStatementList();
+
+        expect('}');
+
+        return delegate.markEnd(delegate.createBlockStatement(block), startToken);
+    }
+
+    // 12.2 Variable Statement
+
+    function parseVariableIdentifier() {
+        var token, startToken;
+
+        startToken = lookahead;
+        token = lex();
+
+        if (token.type !== Token.Identifier) {
+            throwUnexpected(token);
+        }
+
+        return delegate.markEnd(delegate.createIdentifier(token.value), startToken);
+    }
+
+    function parseVariableDeclaration(kind) {
+        var init = null, id, startToken;
+
+        startToken = lookahead;
+        id = parseVariableIdentifier();
+
+        // 12.2.1
+        if (strict && isRestrictedWord(id.name)) {
+            throwErrorTolerant({}, Messages.StrictVarName);
+        }
+
+        if (kind === 'const') {
+            expect('=');
+            init = parseAssignmentExpression();
+        } else if (match('=')) {
+            lex();
+            init = parseAssignmentExpression();
+        }
+
+        return delegate.markEnd(delegate.createVariableDeclarator(id, init), startToken);
+    }
+
+    function parseVariableDeclarationList(kind) {
+        var list = [];
+
+        do {
+            list.push(parseVariableDeclaration(kind));
+            if (!match(',')) {
+                break;
+            }
+            lex();
+        } while (index < length);
+
+        return list;
+    }
+
+    function parseVariableStatement() {
+        var declarations;
+
+        expectKeyword('var');
+
+        declarations = parseVariableDeclarationList();
+
+        consumeSemicolon();
+
+        return delegate.createVariableDeclaration(declarations, 'var');
+    }
+
+    // kind may be `const` or `let`
+    // Both are experimental and not in the specification yet.
+    // see http://wiki.ecmascript.org/doku.php?id=harmony:const
+    // and http://wiki.ecmascript.org/doku.php?id=harmony:let
+    function parseConstLetDeclaration(kind) {
+        var declarations, startToken;
+
+        startToken = lookahead;
+
+        expectKeyword(kind);
+
+        declarations = parseVariableDeclarationList(kind);
+
+        consumeSemicolon();
+
+        return delegate.markEnd(delegate.createVariableDeclaration(declarations, kind), startToken);
+    }
+
+    // 12.3 Empty Statement
+
+    function parseEmptyStatement() {
+        expect(';');
+        return delegate.createEmptyStatement();
+    }
+
+    // 12.4 Expression Statement
+
+    function parseExpressionStatement() {
+        var expr = parseExpression();
+        consumeSemicolon();
+        return delegate.createExpressionStatement(expr);
+    }
+
+    // 12.5 If statement
+
+    function parseIfStatement() {
+        var test, consequent, alternate;
+
+        expectKeyword('if');
+
+        expect('(');
+
+        test = parseExpression();
+
+        expect(')');
+
+        consequent = parseStatement();
+
+        if (matchKeyword('else')) {
+            lex();
+            alternate = parseStatement();
+        } else {
+            alternate = null;
+        }
+
+        return delegate.createIfStatement(test, consequent, alternate);
+    }
+
+    // 12.6 Iteration Statements
+
+    function parseDoWhileStatement() {
+        var body, test, oldInIteration;
+
+        expectKeyword('do');
+
+        oldInIteration = state.inIteration;
+        state.inIteration = true;
+
+        body = parseStatement();
+
+        state.inIteration = oldInIteration;
+
+        expectKeyword('while');
+
+        expect('(');
+
+        test = parseExpression();
+
+        expect(')');
+
+        if (match(';')) {
+            lex();
+        }
+
+        return delegate.createDoWhileStatement(body, test);
+    }
+
+    function parseWhileStatement() {
+        var test, body, oldInIteration;
+
+        expectKeyword('while');
+
+        expect('(');
+
+        test = parseExpression();
+
+        expect(')');
+
+        oldInIteration = state.inIteration;
+        state.inIteration = true;
+
+        body = parseStatement();
+
+        state.inIteration = oldInIteration;
+
+        return delegate.createWhileStatement(test, body);
+    }
+
+    function parseForVariableDeclaration() {
+        var token, declarations, startToken;
+
+        startToken = lookahead;
+        token = lex();
+        declarations = parseVariableDeclarationList();
+
+        return delegate.markEnd(delegate.createVariableDeclaration(declarations, token.value), startToken);
+    }
+
+    function parseForStatement() {
+        var init, test, update, left, right, body, oldInIteration;
+
+        init = test = update = null;
+
+        expectKeyword('for');
+
+        expect('(');
+
+        if (match(';')) {
+            lex();
+        } else {
+            if (matchKeyword('var') || matchKeyword('let')) {
+                state.allowIn = false;
+                init = parseForVariableDeclaration();
+                state.allowIn = true;
+
+                if (init.declarations.length === 1 && matchKeyword('in')) {
+                    lex();
+                    left = init;
+                    right = parseExpression();
+                    init = null;
+                }
+            } else {
+                state.allowIn = false;
+                init = parseExpression();
+                state.allowIn = true;
+
+                if (matchKeyword('in')) {
+                    // LeftHandSideExpression
+                    if (!isLeftHandSide(init)) {
+                        throwErrorTolerant({}, Messages.InvalidLHSInForIn);
+                    }
+
+                    lex();
+                    left = init;
+                    right = parseExpression();
+                    init = null;
+                }
+            }
+
+            if (typeof left === 'undefined') {
+                expect(';');
+            }
+        }
+
+        if (typeof left === 'undefined') {
+
+            if (!match(';')) {
+                test = parseExpression();
+            }
+            expect(';');
+
+            if (!match(')')) {
+                update = parseExpression();
+            }
+        }
+
+        expect(')');
+
+        oldInIteration = state.inIteration;
+        state.inIteration = true;
+
+        body = parseStatement();
+
+        state.inIteration = oldInIteration;
+
+        return (typeof left === 'undefined') ?
+                delegate.createForStatement(init, test, update, body) :
+                delegate.createForInStatement(left, right, body);
+    }
+
+    // 12.7 The continue statement
+
+    function parseContinueStatement() {
+        var label = null, key;
+
+        expectKeyword('continue');
+
+        // Optimize the most common form: 'continue;'.
+        if (source.charCodeAt(index) === 0x3B) {
+            lex();
+
+            if (!state.inIteration) {
+                throwError({}, Messages.IllegalContinue);
+            }
+
+            return delegate.createContinueStatement(null);
+        }
+
+        if (peekLineTerminator()) {
+            if (!state.inIteration) {
+                throwError({}, Messages.IllegalContinue);
+            }
+
+            return delegate.createContinueStatement(null);
+        }
+
+        if (lookahead.type === Token.Identifier) {
+            label = parseVariableIdentifier();
+
+            key = '$' + label.name;
+            if (!Object.prototype.hasOwnProperty.call(state.labelSet, key)) {
+                throwError({}, Messages.UnknownLabel, label.name);
+            }
+        }
+
+        consumeSemicolon();
+
+        if (label === null && !state.inIteration) {
+            throwError({}, Messages.IllegalContinue);
+        }
+
+        return delegate.createContinueStatement(label);
+    }
+
+    // 12.8 The break statement
+
+    function parseBreakStatement() {
+        var label = null, key;
+
+        expectKeyword('break');
+
+        // Catch the very common case first: immediately a semicolon (U+003B).
+        if (source.charCodeAt(index) === 0x3B) {
+            lex();
+
+            if (!(state.inIteration || state.inSwitch)) {
+                throwError({}, Messages.IllegalBreak);
+            }
+
+            return delegate.createBreakStatement(null);
+        }
+
+        if (peekLineTerminator()) {
+            if (!(state.inIteration || state.inSwitch)) {
+                throwError({}, Messages.IllegalBreak);
+            }
+
+            return delegate.createBreakStatement(null);
+        }
+
+        if (lookahead.type === Token.Identifier) {
+            label = parseVariableIdentifier();
+
+            key = '$' + label.name;
+            if (!Object.prototype.hasOwnProperty.call(state.labelSet, key)) {
+                throwError({}, Messages.UnknownLabel, label.name);
+            }
+        }
+
+        consumeSemicolon();
+
+        if (label === null && !(state.inIteration || state.inSwitch)) {
+            throwError({}, Messages.IllegalBreak);
+        }
+
+        return delegate.createBreakStatement(label);
+    }
+
+    // 12.9 The return statement
+
+    function parseReturnStatement() {
+        var argument = null;
+
+        expectKeyword('return');
+
+        if (!state.inFunctionBody) {
+            throwErrorTolerant({}, Messages.IllegalReturn);
+        }
+
+        // 'return' followed by a space and an identifier is very common.
+        if (source.charCodeAt(index) === 0x20) {
+            if (isIdentifierStart(source.charCodeAt(index + 1))) {
+                argument = parseExpression();
+                consumeSemicolon();
+                return delegate.createReturnStatement(argument);
+            }
+        }
+
+        if (peekLineTerminator()) {
+            return delegate.createReturnStatement(null);
+        }
+
+        if (!match(';')) {
+            if (!match('}') && lookahead.type !== Token.EOF) {
+                argument = parseExpression();
+            }
+        }
+
+        consumeSemicolon();
+
+        return delegate.createReturnStatement(argument);
+    }
+
+    // 12.10 The with statement
+
+    function parseWithStatement() {
+        var object, body;
+
+        if (strict) {
+            // TODO(ikarienator): Should we update the test cases instead?
+            skipComment();
+            throwErrorTolerant({}, Messages.StrictModeWith);
+        }
+
+        expectKeyword('with');
+
+        expect('(');
+
+        object = parseExpression();
+
+        expect(')');
+
+        body = parseStatement();
+
+        return delegate.createWithStatement(object, body);
+    }
+
+    // 12.10 The swith statement
+
+    function parseSwitchCase() {
+        var test, consequent = [], statement, startToken;
+
+        startToken = lookahead;
+        if (matchKeyword('default')) {
+            lex();
+            test = null;
+        } else {
+            expectKeyword('case');
+            test = parseExpression();
+        }
+        expect(':');
+
+        while (index < length) {
+            if (match('}') || matchKeyword('default') || matchKeyword('case')) {
+                break;
+            }
+            statement = parseStatement();
+            consequent.push(statement);
+        }
+
+        return delegate.markEnd(delegate.createSwitchCase(test, consequent), startToken);
+    }
+
+    function parseSwitchStatement() {
+        var discriminant, cases, clause, oldInSwitch, defaultFound;
+
+        expectKeyword('switch');
+
+        expect('(');
+
+        discriminant = parseExpression();
+
+        expect(')');
+
+        expect('{');
+
+        cases = [];
+
+        if (match('}')) {
+            lex();
+            return delegate.createSwitchStatement(discriminant, cases);
+        }
+
+        oldInSwitch = state.inSwitch;
+        state.inSwitch = true;
+        defaultFound = false;
+
+        while (index < length) {
+            if (match('}')) {
+                break;
+            }
+            clause = parseSwitchCase();
+            if (clause.test === null) {
+                if (defaultFound) {
+                    throwError({}, Messages.MultipleDefaultsInSwitch);
+                }
+                defaultFound = true;
+            }
+            cases.push(clause);
+        }
+
+        state.inSwitch = oldInSwitch;
+
+        expect('}');
+
+        return delegate.createSwitchStatement(discriminant, cases);
+    }
+
+    // 12.13 The throw statement
+
+    function parseThrowStatement() {
+        var argument;
+
+        expectKeyword('throw');
+
+        if (peekLineTerminator()) {
+            throwError({}, Messages.NewlineAfterThrow);
+        }
+
+        argument = parseExpression();
+
+        consumeSemicolon();
+
+        return delegate.createThrowStatement(argument);
+    }
+
+    // 12.14 The try statement
+
+    function parseCatchClause() {
+        var param, body, startToken;
+
+        startToken = lookahead;
+        expectKeyword('catch');
+
+        expect('(');
+        if (match(')')) {
+            throwUnexpected(lookahead);
+        }
+
+        param = parseVariableIdentifier();
+        // 12.14.1
+        if (strict && isRestrictedWord(param.name)) {
+            throwErrorTolerant({}, Messages.StrictCatchVariable);
+        }
+
+        expect(')');
+        body = parseBlock();
+        return delegate.markEnd(delegate.createCatchClause(param, body), startToken);
+    }
+
+    function parseTryStatement() {
+        var block, handlers = [], finalizer = null;
+
+        expectKeyword('try');
+
+        block = parseBlock();
+
+        if (matchKeyword('catch')) {
+            handlers.push(parseCatchClause());
+        }
+
+        if (matchKeyword('finally')) {
+            lex();
+            finalizer = parseBlock();
+        }
+
+        if (handlers.length === 0 && !finalizer) {
+            throwError({}, Messages.NoCatchOrFinally);
+        }
+
+        return delegate.createTryStatement(block, [], handlers, finalizer);
+    }
+
+    // 12.15 The debugger statement
+
+    function parseDebuggerStatement() {
+        expectKeyword('debugger');
+
+        consumeSemicolon();
+
+        return delegate.createDebuggerStatement();
+    }
+
+    // 12 Statements
+
+    function parseStatement() {
+        var type = lookahead.type,
+            expr,
+            labeledBody,
+            key,
+            startToken;
+
+        if (type === Token.EOF) {
+            throwUnexpected(lookahead);
+        }
+
+        if (type === Token.Punctuator && lookahead.value === '{') {
+            return parseBlock();
+        }
+
+        startToken = lookahead;
+
+        if (type === Token.Punctuator) {
+            switch (lookahead.value) {
+            case ';':
+                return delegate.markEnd(parseEmptyStatement(), startToken);
+            case '(':
+                return delegate.markEnd(parseExpressionStatement(), startToken);
+            default:
+                break;
+            }
+        }
+
+        if (type === Token.Keyword) {
+            switch (lookahead.value) {
+            case 'break':
+                return delegate.markEnd(parseBreakStatement(), startToken);
+            case 'continue':
+                return delegate.markEnd(parseContinueStatement(), startToken);
+            case 'debugger':
+                return delegate.markEnd(parseDebuggerStatement(), startToken);
+            case 'do':
+                return delegate.markEnd(parseDoWhileStatement(), startToken);
+            case 'for':
+                return delegate.markEnd(parseForStatement(), startToken);
+            case 'function':
+                return delegate.markEnd(parseFunctionDeclaration(), startToken);
+            case 'if':
+                return delegate.markEnd(parseIfStatement(), startToken);
+            case 'return':
+                return delegate.markEnd(parseReturnStatement(), startToken);
+            case 'switch':
+                return delegate.markEnd(parseSwitchStatement(), startToken);
+            case 'throw':
+                return delegate.markEnd(parseThrowStatement(), startToken);
+            case 'try':
+                return delegate.markEnd(parseTryStatement(), startToken);
+            case 'var':
+                return delegate.markEnd(parseVariableStatement(), startToken);
+            case 'while':
+                return delegate.markEnd(parseWhileStatement(), startToken);
+            case 'with':
+                return delegate.markEnd(parseWithStatement(), startToken);
+            default:
+                break;
+            }
+        }
+
+        expr = parseExpression();
+
+        // 12.12 Labelled Statements
+        if ((expr.type === Syntax.Identifier) && match(':')) {
+            lex();
+
+            key = '$' + expr.name;
+            if (Object.prototype.hasOwnProperty.call(state.labelSet, key)) {
+                throwError({}, Messages.Redeclaration, 'Label', expr.name);
+            }
+
+            state.labelSet[key] = true;
+            labeledBody = parseStatement();
+            delete state.labelSet[key];
+            return delegate.markEnd(delegate.createLabeledStatement(expr, labeledBody), startToken);
+        }
+
+        consumeSemicolon();
+
+        return delegate.markEnd(delegate.createExpressionStatement(expr), startToken);
+    }
+
+    // 13 Function Definition
+
+    function parseFunctionSourceElements() {
+        var sourceElement, sourceElements = [], token, directive, firstRestricted,
+            oldLabelSet, oldInIteration, oldInSwitch, oldInFunctionBody, startToken;
+
+        startToken = lookahead;
+        expect('{');
+
+        while (index < length) {
+            if (lookahead.type !== Token.StringLiteral) {
+                break;
+            }
+            token = lookahead;
+
+            sourceElement = parseSourceElement();
+            sourceElements.push(sourceElement);
+            if (sourceElement.expression.type !== Syntax.Literal) {
+                // this is not directive
+                break;
+            }
+            directive = source.slice(token.start + 1, token.end - 1);
+            if (directive === 'use strict') {
+                strict = true;
+                if (firstRestricted) {
+                    throwErrorTolerant(firstRestricted, Messages.StrictOctalLiteral);
+                }
+            } else {
+                if (!firstRestricted && token.octal) {
+                    firstRestricted = token;
+                }
+            }
+        }
+
+        oldLabelSet = state.labelSet;
+        oldInIteration = state.inIteration;
+        oldInSwitch = state.inSwitch;
+        oldInFunctionBody = state.inFunctionBody;
+
+        state.labelSet = {};
+        state.inIteration = false;
+        state.inSwitch = false;
+        state.inFunctionBody = true;
+
+        while (index < length) {
+            if (match('}')) {
+                break;
+            }
+            sourceElement = parseSourceElement();
+            if (typeof sourceElement === 'undefined') {
+                break;
+            }
+            sourceElements.push(sourceElement);
+        }
+
+        expect('}');
+
+        state.labelSet = oldLabelSet;
+        state.inIteration = oldInIteration;
+        state.inSwitch = oldInSwitch;
+        state.inFunctionBody = oldInFunctionBody;
+
+        return delegate.markEnd(delegate.createBlockStatement(sourceElements), startToken);
+    }
+
+    function parseParams(firstRestricted) {
+        var param, params = [], token, stricted, paramSet, key, message;
+        expect('(');
+
+        if (!match(')')) {
+            paramSet = {};
+            while (index < length) {
+                token = lookahead;
+                param = parseVariableIdentifier();
+                key = '$' + token.value;
+                if (strict) {
+                    if (isRestrictedWord(token.value)) {
+                        stricted = token;
+                        message = Messages.StrictParamName;
+                    }
+                    if (Object.prototype.hasOwnProperty.call(paramSet, key)) {
+                        stricted = token;
+                        message = Messages.StrictParamDupe;
+                    }
+                } else if (!firstRestricted) {
+                    if (isRestrictedWord(token.value)) {
+                        firstRestricted = token;
+                        message = Messages.StrictParamName;
+                    } else if (isStrictModeReservedWord(token.value)) {
+                        firstRestricted = token;
+                        message = Messages.StrictReservedWord;
+                    } else if (Object.prototype.hasOwnProperty.call(paramSet, key)) {
+                        firstRestricted = token;
+                        message = Messages.StrictParamDupe;
+                    }
+                }
+                params.push(param);
+                paramSet[key] = true;
+                if (match(')')) {
+                    break;
+                }
+                expect(',');
+            }
+        }
+
+        expect(')');
+
+        return {
+            params: params,
+            stricted: stricted,
+            firstRestricted: firstRestricted,
+            message: message
+        };
+    }
+
+    function parseFunctionDeclaration() {
+        var id, params = [], body, token, stricted, tmp, firstRestricted, message, previousStrict, startToken;
+
+        startToken = lookahead;
+
+        expectKeyword('function');
+        token = lookahead;
+        id = parseVariableIdentifier();
+        if (strict) {
+            if (isRestrictedWord(token.value)) {
+                throwErrorTolerant(token, Messages.StrictFunctionName);
+            }
+        } else {
+            if (isRestrictedWord(token.value)) {
+                firstRestricted = token;
+                message = Messages.StrictFunctionName;
+            } else if (isStrictModeReservedWord(token.value)) {
+                firstRestricted = token;
+                message = Messages.StrictReservedWord;
+            }
+        }
+
+        tmp = parseParams(firstRestricted);
+        params = tmp.params;
+        stricted = tmp.stricted;
+        firstRestricted = tmp.firstRestricted;
+        if (tmp.message) {
+            message = tmp.message;
+        }
+
+        previousStrict = strict;
+        body = parseFunctionSourceElements();
+        if (strict && firstRestricted) {
+            throwError(firstRestricted, message);
+        }
+        if (strict && stricted) {
+            throwErrorTolerant(stricted, message);
+        }
+        strict = previousStrict;
+
+        return delegate.markEnd(delegate.createFunctionDeclaration(id, params, [], body), startToken);
+    }
+
+    function parseFunctionExpression() {
+        var token, id = null, stricted, firstRestricted, message, tmp, params = [], body, previousStrict, startToken;
+
+        startToken = lookahead;
+        expectKeyword('function');
+
+        if (!match('(')) {
+            token = lookahead;
+            id = parseVariableIdentifier();
+            if (strict) {
+                if (isRestrictedWord(token.value)) {
+                    throwErrorTolerant(token, Messages.StrictFunctionName);
+                }
+            } else {
+                if (isRestrictedWord(token.value)) {
+                    firstRestricted = token;
+                    message = Messages.StrictFunctionName;
+                } else if (isStrictModeReservedWord(token.value)) {
+                    firstRestricted = token;
+                    message = Messages.StrictReservedWord;
+                }
+            }
+        }
+
+        tmp = parseParams(firstRestricted);
+        params = tmp.params;
+        stricted = tmp.stricted;
+        firstRestricted = tmp.firstRestricted;
+        if (tmp.message) {
+            message = tmp.message;
+        }
+
+        previousStrict = strict;
+        body = parseFunctionSourceElements();
+        if (strict && firstRestricted) {
+            throwError(firstRestricted, message);
+        }
+        if (strict && stricted) {
+            throwErrorTolerant(stricted, message);
+        }
+        strict = previousStrict;
+
+        return delegate.markEnd(delegate.createFunctionExpression(id, params, [], body), startToken);
+    }
+
+    // 14 Program
+
+    function parseSourceElement() {
+        if (lookahead.type === Token.Keyword) {
+            switch (lookahead.value) {
+            case 'const':
+            case 'let':
+                return parseConstLetDeclaration(lookahead.value);
+            case 'function':
+                return parseFunctionDeclaration();
+            default:
+                return parseStatement();
+            }
+        }
+
+        if (lookahead.type !== Token.EOF) {
+            return parseStatement();
+        }
+    }
+
+    function parseSourceElements() {
+        var sourceElement, sourceElements = [], token, directive, firstRestricted;
+
+        while (index < length) {
+            token = lookahead;
+            if (token.type !== Token.StringLiteral) {
+                break;
+            }
+
+            sourceElement = parseSourceElement();
+            sourceElements.push(sourceElement);
+            if (sourceElement.expression.type !== Syntax.Literal) {
+                // this is not directive
+                break;
+            }
+            directive = source.slice(token.start + 1, token.end - 1);
+            if (directive === 'use strict') {
+                strict = true;
+                if (firstRestricted) {
+                    throwErrorTolerant(firstRestricted, Messages.StrictOctalLiteral);
+                }
+            } else {
+                if (!firstRestricted && token.octal) {
+                    firstRestricted = token;
+                }
+            }
+        }
+
+        while (index < length) {
+            sourceElement = parseSourceElement();
+            /* istanbul ignore if */
+            if (typeof sourceElement === 'undefined') {
+                break;
+            }
+            sourceElements.push(sourceElement);
+        }
+        return sourceElements;
+    }
+
+    function parseProgram() {
+        var body, startToken;
+
+        skipComment();
+        peek();
+        startToken = lookahead;
+        strict = false;
+
+        body = parseSourceElements();
+        return delegate.markEnd(delegate.createProgram(body), startToken);
+    }
+
+    function filterTokenLocation() {
+        var i, entry, token, tokens = [];
+
+        for (i = 0; i < extra.tokens.length; ++i) {
+            entry = extra.tokens[i];
+            token = {
+                type: entry.type,
+                value: entry.value
+            };
+            if (extra.range) {
+                token.range = entry.range;
+            }
+            if (extra.loc) {
+                token.loc = entry.loc;
+            }
+            tokens.push(token);
+        }
+
+        extra.tokens = tokens;
+    }
+
+    function tokenize(code, options) {
+        var toString,
+            token,
+            tokens;
+
+        toString = String;
+        if (typeof code !== 'string' && !(code instanceof String)) {
+            code = toString(code);
+        }
+
+        delegate = SyntaxTreeDelegate;
+        source = code;
+        index = 0;
+        lineNumber = (source.length > 0) ? 1 : 0;
+        lineStart = 0;
+        length = source.length;
+        lookahead = null;
+        state = {
+            allowIn: true,
+            labelSet: {},
+            inFunctionBody: false,
+            inIteration: false,
+            inSwitch: false,
+            lastCommentStart: -1
+        };
+
+        extra = {};
+
+        // Options matching.
+        options = options || {};
+
+        // Of course we collect tokens here.
+        options.tokens = true;
+        extra.tokens = [];
+        extra.tokenize = true;
+        // The following two fields are necessary to compute the Regex tokens.
+        extra.openParenToken = -1;
+        extra.openCurlyToken = -1;
+
+        extra.range = (typeof options.range === 'boolean') && options.range;
+        extra.loc = (typeof options.loc === 'boolean') && options.loc;
+
+        if (typeof options.comment === 'boolean' && options.comment) {
+            extra.comments = [];
+        }
+        if (typeof options.tolerant === 'boolean' && options.tolerant) {
+            extra.errors = [];
+        }
+
+        try {
+            peek();
+            if (lookahead.type === Token.EOF) {
+                return extra.tokens;
+            }
+
+            token = lex();
+            while (lookahead.type !== Token.EOF) {
+                try {
+                    token = lex();
+                } catch (lexError) {
+                    token = lookahead;
+                    if (extra.errors) {
+                        extra.errors.push(lexError);
+                        // We have to break on the first error
+                        // to avoid infinite loops.
+                        break;
+                    } else {
+                        throw lexError;
+                    }
+                }
+            }
+
+            filterTokenLocation();
+            tokens = extra.tokens;
+            if (typeof extra.comments !== 'undefined') {
+                tokens.comments = extra.comments;
+            }
+            if (typeof extra.errors !== 'undefined') {
+                tokens.errors = extra.errors;
+            }
+        } catch (e) {
+            throw e;
+        } finally {
+            extra = {};
+        }
+        return tokens;
+    }
+
+    function parse(code, options) {
+        var program, toString;
+
+        toString = String;
+        if (typeof code !== 'string' && !(code instanceof String)) {
+            code = toString(code);
+        }
+
+        delegate = SyntaxTreeDelegate;
+        source = code;
+        index = 0;
+        lineNumber = (source.length > 0) ? 1 : 0;
+        lineStart = 0;
+        length = source.length;
+        lookahead = null;
+        state = {
+            allowIn: true,
+            labelSet: {},
+            inFunctionBody: false,
+            inIteration: false,
+            inSwitch: false,
+            lastCommentStart: -1
+        };
+
+        extra = {};
+        if (typeof options !== 'undefined') {
+            extra.range = (typeof options.range === 'boolean') && options.range;
+            extra.loc = (typeof options.loc === 'boolean') && options.loc;
+            extra.attachComment = (typeof options.attachComment === 'boolean') && options.attachComment;
+
+            if (extra.loc && options.source !== null && options.source !== undefined) {
+                extra.source = toString(options.source);
+            }
+
+            if (typeof options.tokens === 'boolean' && options.tokens) {
+                extra.tokens = [];
+            }
+            if (typeof options.comment === 'boolean' && options.comment) {
+                extra.comments = [];
+            }
+            if (typeof options.tolerant === 'boolean' && options.tolerant) {
+                extra.errors = [];
+            }
+            if (extra.attachComment) {
+                extra.range = true;
+                extra.comments = [];
+                extra.bottomRightStack = [];
+                extra.trailingComments = [];
+                extra.leadingComments = [];
+            }
+        }
+
+        try {
+            program = parseProgram();
+            if (typeof extra.comments !== 'undefined') {
+                program.comments = extra.comments;
+            }
+            if (typeof extra.tokens !== 'undefined') {
+                filterTokenLocation();
+                program.tokens = extra.tokens;
+            }
+            if (typeof extra.errors !== 'undefined') {
+                program.errors = extra.errors;
+            }
+        } catch (e) {
+            throw e;
+        } finally {
+            extra = {};
+        }
+
+        return program;
+    }
+
+    // Sync with *.json manifests.
+    exports.version = '1.2.2';
+
+    exports.tokenize = tokenize;
+
+    exports.parse = parse;
+
+    // Deep copy.
+   /* istanbul ignore next */
+    exports.Syntax = (function () {
+        var name, types = {};
+
+        if (typeof Object.create === 'function') {
+            types = Object.create(null);
+        }
+
+        for (name in Syntax) {
+            if (Syntax.hasOwnProperty(name)) {
+                types[name] = Syntax[name];
+            }
+        }
+
+        if (typeof Object.freeze === 'function') {
+            Object.freeze(types);
+        }
+
+        return types;
+    }());
+
+}));
+/* vim: set sw=4 ts=4 et tw=80 : */
diff --git a/packages/context-coloring/languages/javascript/libraries/estraverse.js b/packages/context-coloring/languages/javascript/libraries/estraverse.js
new file mode 100644
index 0000000..4009cb0
--- /dev/null
+++ b/packages/context-coloring/languages/javascript/libraries/estraverse.js
@@ -0,0 +1,838 @@
+/*
+  Copyright (C) 2012-2013 Yusuke Suzuki <utatane.tea@gmail.com>
+  Copyright (C) 2012 Ariya Hidayat <ariya.hidayat@gmail.com>
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above copyright
+      notice, this list of conditions and the following disclaimer in the
+      documentation and/or other materials provided with the distribution.
+
+  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+  ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
+  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+/*jslint vars:false, bitwise:true*/
+/*jshint indent:4*/
+/*global exports:true, define:true*/
+(function (root, factory) {
+    'use strict';
+
+    // Universal Module Definition (UMD) to support AMD, CommonJS/Node.js,
+    // and plain browser loading,
+    if (typeof define === 'function' && define.amd) {
+        define(['exports'], factory);
+    } else if (typeof exports !== 'undefined') {
+        factory(exports);
+    } else {
+        factory((root.estraverse = {}));
+    }
+}(this, function (exports) {
+    'use strict';
+
+    var Syntax,
+        isArray,
+        VisitorOption,
+        VisitorKeys,
+        objectCreate,
+        objectKeys,
+        BREAK,
+        SKIP,
+        REMOVE;
+
+    function ignoreJSHintError() { }
+
+    isArray = Array.isArray;
+    if (!isArray) {
+        isArray = function isArray(array) {
+            return Object.prototype.toString.call(array) === '[object Array]';
+        };
+    }
+
+    function deepCopy(obj) {
+        var ret = {}, key, val;
+        for (key in obj) {
+            if (obj.hasOwnProperty(key)) {
+                val = obj[key];
+                if (typeof val === 'object' && val !== null) {
+                    ret[key] = deepCopy(val);
+                } else {
+                    ret[key] = val;
+                }
+            }
+        }
+        return ret;
+    }
+
+    function shallowCopy(obj) {
+        var ret = {}, key;
+        for (key in obj) {
+            if (obj.hasOwnProperty(key)) {
+                ret[key] = obj[key];
+            }
+        }
+        return ret;
+    }
+    ignoreJSHintError(shallowCopy);
+
+    // based on LLVM libc++ upper_bound / lower_bound
+    // MIT License
+
+    function upperBound(array, func) {
+        var diff, len, i, current;
+
+        len = array.length;
+        i = 0;
+
+        while (len) {
+            diff = len >>> 1;
+            current = i + diff;
+            if (func(array[current])) {
+                len = diff;
+            } else {
+                i = current + 1;
+                len -= diff + 1;
+            }
+        }
+        return i;
+    }
+
+    function lowerBound(array, func) {
+        var diff, len, i, current;
+
+        len = array.length;
+        i = 0;
+
+        while (len) {
+            diff = len >>> 1;
+            current = i + diff;
+            if (func(array[current])) {
+                i = current + 1;
+                len -= diff + 1;
+            } else {
+                len = diff;
+            }
+        }
+        return i;
+    }
+    ignoreJSHintError(lowerBound);
+
+    objectCreate = Object.create || (function () {
+        function F() { }
+
+        return function (o) {
+            F.prototype = o;
+            return new F();
+        };
+    })();
+
+    objectKeys = Object.keys || function (o) {
+        var keys = [], key;
+        for (key in o) {
+            keys.push(key);
+        }
+        return keys;
+    };
+
+    function extend(to, from) {
+        objectKeys(from).forEach(function (key) {
+            to[key] = from[key];
+        });
+        return to;
+    }
+
+    Syntax = {
+        AssignmentExpression: 'AssignmentExpression',
+        ArrayExpression: 'ArrayExpression',
+        ArrayPattern: 'ArrayPattern',
+        ArrowFunctionExpression: 'ArrowFunctionExpression',
+        BlockStatement: 'BlockStatement',
+        BinaryExpression: 'BinaryExpression',
+        BreakStatement: 'BreakStatement',
+        CallExpression: 'CallExpression',
+        CatchClause: 'CatchClause',
+        ClassBody: 'ClassBody',
+        ClassDeclaration: 'ClassDeclaration',
+        ClassExpression: 'ClassExpression',
+        ComprehensionBlock: 'ComprehensionBlock',  // CAUTION: It's deferred to ES7.
+        ComprehensionExpression: 'ComprehensionExpression',  // CAUTION: It's deferred to ES7.
+        ConditionalExpression: 'ConditionalExpression',
+        ContinueStatement: 'ContinueStatement',
+        DebuggerStatement: 'DebuggerStatement',
+        DirectiveStatement: 'DirectiveStatement',
+        DoWhileStatement: 'DoWhileStatement',
+        EmptyStatement: 'EmptyStatement',
+        ExportBatchSpecifier: 'ExportBatchSpecifier',
+        ExportDeclaration: 'ExportDeclaration',
+        ExportSpecifier: 'ExportSpecifier',
+        ExpressionStatement: 'ExpressionStatement',
+        ForStatement: 'ForStatement',
+        ForInStatement: 'ForInStatement',
+        ForOfStatement: 'ForOfStatement',
+        FunctionDeclaration: 'FunctionDeclaration',
+        FunctionExpression: 'FunctionExpression',
+        GeneratorExpression: 'GeneratorExpression',  // CAUTION: It's deferred to ES7.
+        Identifier: 'Identifier',
+        IfStatement: 'IfStatement',
+        ImportDeclaration: 'ImportDeclaration',
+        ImportDefaultSpecifier: 'ImportDefaultSpecifier',
+        ImportNamespaceSpecifier: 'ImportNamespaceSpecifier',
+        ImportSpecifier: 'ImportSpecifier',
+        Literal: 'Literal',
+        LabeledStatement: 'LabeledStatement',
+        LogicalExpression: 'LogicalExpression',
+        MemberExpression: 'MemberExpression',
+        MethodDefinition: 'MethodDefinition',
+        ModuleSpecifier: 'ModuleSpecifier',
+        NewExpression: 'NewExpression',
+        ObjectExpression: 'ObjectExpression',
+        ObjectPattern: 'ObjectPattern',
+        Program: 'Program',
+        Property: 'Property',
+        ReturnStatement: 'ReturnStatement',
+        SequenceExpression: 'SequenceExpression',
+        SpreadElement: 'SpreadElement',
+        SwitchStatement: 'SwitchStatement',
+        SwitchCase: 'SwitchCase',
+        TaggedTemplateExpression: 'TaggedTemplateExpression',
+        TemplateElement: 'TemplateElement',
+        TemplateLiteral: 'TemplateLiteral',
+        ThisExpression: 'ThisExpression',
+        ThrowStatement: 'ThrowStatement',
+        TryStatement: 'TryStatement',
+        UnaryExpression: 'UnaryExpression',
+        UpdateExpression: 'UpdateExpression',
+        VariableDeclaration: 'VariableDeclaration',
+        VariableDeclarator: 'VariableDeclarator',
+        WhileStatement: 'WhileStatement',
+        WithStatement: 'WithStatement',
+        YieldExpression: 'YieldExpression'
+    };
+
+    VisitorKeys = {
+        AssignmentExpression: ['left', 'right'],
+        ArrayExpression: ['elements'],
+        ArrayPattern: ['elements'],
+        ArrowFunctionExpression: ['params', 'defaults', 'rest', 'body'],
+        BlockStatement: ['body'],
+        BinaryExpression: ['left', 'right'],
+        BreakStatement: ['label'],
+        CallExpression: ['callee', 'arguments'],
+        CatchClause: ['param', 'body'],
+        ClassBody: ['body'],
+        ClassDeclaration: ['id', 'body', 'superClass'],
+        ClassExpression: ['id', 'body', 'superClass'],
+        ComprehensionBlock: ['left', 'right'],  // CAUTION: It's deferred to ES7.
+        ComprehensionExpression: ['blocks', 'filter', 'body'],  // CAUTION: It's deferred to ES7.
+        ConditionalExpression: ['test', 'consequent', 'alternate'],
+        ContinueStatement: ['label'],
+        DebuggerStatement: [],
+        DirectiveStatement: [],
+        DoWhileStatement: ['body', 'test'],
+        EmptyStatement: [],
+        ExportBatchSpecifier: [],
+        ExportDeclaration: ['declaration', 'specifiers', 'source'],
+        ExportSpecifier: ['id', 'name'],
+        ExpressionStatement: ['expression'],
+        ForStatement: ['init', 'test', 'update', 'body'],
+        ForInStatement: ['left', 'right', 'body'],
+        ForOfStatement: ['left', 'right', 'body'],
+        FunctionDeclaration: ['id', 'params', 'defaults', 'rest', 'body'],
+        FunctionExpression: ['id', 'params', 'defaults', 'rest', 'body'],
+        GeneratorExpression: ['blocks', 'filter', 'body'],  // CAUTION: It's deferred to ES7.
+        Identifier: [],
+        IfStatement: ['test', 'consequent', 'alternate'],
+        ImportDeclaration: ['specifiers', 'source'],
+        ImportDefaultSpecifier: ['id'],
+        ImportNamespaceSpecifier: ['id'],
+        ImportSpecifier: ['id', 'name'],
+        Literal: [],
+        LabeledStatement: ['label', 'body'],
+        LogicalExpression: ['left', 'right'],
+        MemberExpression: ['object', 'property'],
+        MethodDefinition: ['key', 'value'],
+        ModuleSpecifier: [],
+        NewExpression: ['callee', 'arguments'],
+        ObjectExpression: ['properties'],
+        ObjectPattern: ['properties'],
+        Program: ['body'],
+        Property: ['key', 'value'],
+        ReturnStatement: ['argument'],
+        SequenceExpression: ['expressions'],
+        SpreadElement: ['argument'],
+        SwitchStatement: ['discriminant', 'cases'],
+        SwitchCase: ['test', 'consequent'],
+        TaggedTemplateExpression: ['tag', 'quasi'],
+        TemplateElement: [],
+        TemplateLiteral: ['quasis', 'expressions'],
+        ThisExpression: [],
+        ThrowStatement: ['argument'],
+        TryStatement: ['block', 'handlers', 'handler', 'guardedHandlers', 'finalizer'],
+        UnaryExpression: ['argument'],
+        UpdateExpression: ['argument'],
+        VariableDeclaration: ['declarations'],
+        VariableDeclarator: ['id', 'init'],
+        WhileStatement: ['test', 'body'],
+        WithStatement: ['object', 'body'],
+        YieldExpression: ['argument']
+    };
+
+    // unique id
+    BREAK = {};
+    SKIP = {};
+    REMOVE = {};
+
+    VisitorOption = {
+        Break: BREAK,
+        Skip: SKIP,
+        Remove: REMOVE
+    };
+
+    function Reference(parent, key) {
+        this.parent = parent;
+        this.key = key;
+    }
+
+    Reference.prototype.replace = function replace(node) {
+        this.parent[this.key] = node;
+    };
+
+    Reference.prototype.remove = function remove() {
+        if (isArray(this.parent)) {
+            this.parent.splice(this.key, 1);
+            return true;
+        } else {
+            this.replace(null);
+            return false;
+        }
+    };
+
+    function Element(node, path, wrap, ref) {
+        this.node = node;
+        this.path = path;
+        this.wrap = wrap;
+        this.ref = ref;
+    }
+
+    function Controller() { }
+
+    // API:
+    // return property path array from root to current node
+    Controller.prototype.path = function path() {
+        var i, iz, j, jz, result, element;
+
+        function addToPath(result, path) {
+            if (isArray(path)) {
+                for (j = 0, jz = path.length; j < jz; ++j) {
+                    result.push(path[j]);
+                }
+            } else {
+                result.push(path);
+            }
+        }
+
+        // root node
+        if (!this.__current.path) {
+            return null;
+        }
+
+        // first node is sentinel, second node is root element
+        result = [];
+        for (i = 2, iz = this.__leavelist.length; i < iz; ++i) {
+            element = this.__leavelist[i];
+            addToPath(result, element.path);
+        }
+        addToPath(result, this.__current.path);
+        return result;
+    };
+
+    // API:
+    // return type of current node
+    Controller.prototype.type = function () {
+        var node = this.current();
+        return node.type || this.__current.wrap;
+    };
+
+    // API:
+    // return array of parent elements
+    Controller.prototype.parents = function parents() {
+        var i, iz, result;
+
+        // first node is sentinel
+        result = [];
+        for (i = 1, iz = this.__leavelist.length; i < iz; ++i) {
+            result.push(this.__leavelist[i].node);
+        }
+
+        return result;
+    };
+
+    // API:
+    // return current node
+    Controller.prototype.current = function current() {
+        return this.__current.node;
+    };
+
+    Controller.prototype.__execute = function __execute(callback, element) {
+        var previous, result;
+
+        result = undefined;
+
+        previous  = this.__current;
+        this.__current = element;
+        this.__state = null;
+        if (callback) {
+            result = callback.call(this, element.node, this.__leavelist[this.__leavelist.length - 1].node);
+        }
+        this.__current = previous;
+
+        return result;
+    };
+
+    // API:
+    // notify control skip / break
+    Controller.prototype.notify = function notify(flag) {
+        this.__state = flag;
+    };
+
+    // API:
+    // skip child nodes of current node
+    Controller.prototype.skip = function () {
+        this.notify(SKIP);
+    };
+
+    // API:
+    // break traversals
+    Controller.prototype['break'] = function () {
+        this.notify(BREAK);
+    };
+
+    // API:
+    // remove node
+    Controller.prototype.remove = function () {
+        this.notify(REMOVE);
+    };
+
+    Controller.prototype.__initialize = function(root, visitor) {
+        this.visitor = visitor;
+        this.root = root;
+        this.__worklist = [];
+        this.__leavelist = [];
+        this.__current = null;
+        this.__state = null;
+        this.__fallback = visitor.fallback === 'iteration';
+        this.__keys = VisitorKeys;
+        if (visitor.keys) {
+            this.__keys = extend(objectCreate(this.__keys), visitor.keys);
+        }
+    };
+
+    function isNode(node) {
+        if (node == null) {
+            return false;
+        }
+        return typeof node === 'object' && typeof node.type === 'string';
+    }
+
+    function isProperty(nodeType, key) {
+        return (nodeType === Syntax.ObjectExpression || nodeType === Syntax.ObjectPattern) && 'properties' === key;
+    }
+
+    Controller.prototype.traverse = function traverse(root, visitor) {
+        var worklist,
+            leavelist,
+            element,
+            node,
+            nodeType,
+            ret,
+            key,
+            current,
+            current2,
+            candidates,
+            candidate,
+            sentinel;
+
+        this.__initialize(root, visitor);
+
+        sentinel = {};
+
+        // reference
+        worklist = this.__worklist;
+        leavelist = this.__leavelist;
+
+        // initialize
+        worklist.push(new Element(root, null, null, null));
+        leavelist.push(new Element(null, null, null, null));
+
+        while (worklist.length) {
+            element = worklist.pop();
+
+            if (element === sentinel) {
+                element = leavelist.pop();
+
+                ret = this.__execute(visitor.leave, element);
+
+                if (this.__state === BREAK || ret === BREAK) {
+                    return;
+                }
+                continue;
+            }
+
+            if (element.node) {
+
+                ret = this.__execute(visitor.enter, element);
+
+                if (this.__state === BREAK || ret === BREAK) {
+                    return;
+                }
+
+                worklist.push(sentinel);
+                leavelist.push(element);
+
+                if (this.__state === SKIP || ret === SKIP) {
+                    continue;
+                }
+
+                node = element.node;
+                nodeType = element.wrap || node.type;
+                candidates = this.__keys[nodeType];
+                if (!candidates) {
+                    if (this.__fallback) {
+                        candidates = objectKeys(node);
+                    } else {
+                        throw new Error('Unknown node type ' + nodeType + '.');
+                    }
+                }
+
+                current = candidates.length;
+                while ((current -= 1) >= 0) {
+                    key = candidates[current];
+                    candidate = node[key];
+                    if (!candidate) {
+                        continue;
+                    }
+
+                    if (isArray(candidate)) {
+                        current2 = candidate.length;
+                        while ((current2 -= 1) >= 0) {
+                            if (!candidate[current2]) {
+                                continue;
+                            }
+                            if (isProperty(nodeType, candidates[current])) {
+                                element = new Element(candidate[current2], [key, current2], 'Property', null);
+                            } else if (isNode(candidate[current2])) {
+                                element = new Element(candidate[current2], [key, current2], null, null);
+                            } else {
+                                continue;
+                            }
+                            worklist.push(element);
+                        }
+                    } else if (isNode(candidate)) {
+                        worklist.push(new Element(candidate, key, null, null));
+                    }
+                }
+            }
+        }
+    };
+
+    Controller.prototype.replace = function replace(root, visitor) {
+        function removeElem(element) {
+            var i,
+                key,
+                nextElem,
+                parent;
+
+            if (element.ref.remove()) {
+                // When the reference is an element of an array.
+                key = element.ref.key;
+                parent = element.ref.parent;
+
+                // If removed from array, then decrease following items' keys.
+                i = worklist.length;
+                while (i--) {
+                    nextElem = worklist[i];
+                    if (nextElem.ref && nextElem.ref.parent === parent) {
+                        if  (nextElem.ref.key < key) {
+                            break;
+                        }
+                        --nextElem.ref.key;
+                    }
+                }
+            }
+        }
+
+        var worklist,
+            leavelist,
+            node,
+            nodeType,
+            target,
+            element,
+            current,
+            current2,
+            candidates,
+            candidate,
+            sentinel,
+            outer,
+            key;
+
+        this.__initialize(root, visitor);
+
+        sentinel = {};
+
+        // reference
+        worklist = this.__worklist;
+        leavelist = this.__leavelist;
+
+        // initialize
+        outer = {
+            root: root
+        };
+        element = new Element(root, null, null, new Reference(outer, 'root'));
+        worklist.push(element);
+        leavelist.push(element);
+
+        while (worklist.length) {
+            element = worklist.pop();
+
+            if (element === sentinel) {
+                element = leavelist.pop();
+
+                target = this.__execute(visitor.leave, element);
+
+                // node may be replaced with null,
+                // so distinguish between undefined and null in this place
+                if (target !== undefined && target !== BREAK && target !== SKIP && target !== REMOVE) {
+                    // replace
+                    element.ref.replace(target);
+                }
+
+                if (this.__state === REMOVE || target === REMOVE) {
+                    removeElem(element);
+                }
+
+                if (this.__state === BREAK || target === BREAK) {
+                    return outer.root;
+                }
+                continue;
+            }
+
+            target = this.__execute(visitor.enter, element);
+
+            // node may be replaced with null,
+            // so distinguish between undefined and null in this place
+            if (target !== undefined && target !== BREAK && target !== SKIP && target !== REMOVE) {
+                // replace
+                element.ref.replace(target);
+                element.node = target;
+            }
+
+            if (this.__state === REMOVE || target === REMOVE) {
+                removeElem(element);
+                element.node = null;
+            }
+
+            if (this.__state === BREAK || target === BREAK) {
+                return outer.root;
+            }
+
+            // node may be null
+            node = element.node;
+            if (!node) {
+                continue;
+            }
+
+            worklist.push(sentinel);
+            leavelist.push(element);
+
+            if (this.__state === SKIP || target === SKIP) {
+                continue;
+            }
+
+            nodeType = element.wrap || node.type;
+            candidates = this.__keys[nodeType];
+            if (!candidates) {
+                if (this.__fallback) {
+                    candidates = objectKeys(node);
+                } else {
+                    throw new Error('Unknown node type ' + nodeType + '.');
+                }
+            }
+
+            current = candidates.length;
+            while ((current -= 1) >= 0) {
+                key = candidates[current];
+                candidate = node[key];
+                if (!candidate) {
+                    continue;
+                }
+
+                if (isArray(candidate)) {
+                    current2 = candidate.length;
+                    while ((current2 -= 1) >= 0) {
+                        if (!candidate[current2]) {
+                            continue;
+                        }
+                        if (isProperty(nodeType, candidates[current])) {
+                            element = new Element(candidate[current2], [key, current2], 'Property', new Reference(candidate, current2));
+                        } else if (isNode(candidate[current2])) {
+                            element = new Element(candidate[current2], [key, current2], null, new Reference(candidate, current2));
+                        } else {
+                            continue;
+                        }
+                        worklist.push(element);
+                    }
+                } else if (isNode(candidate)) {
+                    worklist.push(new Element(candidate, key, null, new Reference(node, key)));
+                }
+            }
+        }
+
+        return outer.root;
+    };
+
+    function traverse(root, visitor) {
+        var controller = new Controller();
+        return controller.traverse(root, visitor);
+    }
+
+    function replace(root, visitor) {
+        var controller = new Controller();
+        return controller.replace(root, visitor);
+    }
+
+    function extendCommentRange(comment, tokens) {
+        var target;
+
+        target = upperBound(tokens, function search(token) {
+            return token.range[0] > comment.range[0];
+        });
+
+        comment.extendedRange = [comment.range[0], comment.range[1]];
+
+        if (target !== tokens.length) {
+            comment.extendedRange[1] = tokens[target].range[0];
+        }
+
+        target -= 1;
+        if (target >= 0) {
+            comment.extendedRange[0] = tokens[target].range[1];
+        }
+
+        return comment;
+    }
+
+    function attachComments(tree, providedComments, tokens) {
+        // At first, we should calculate extended comment ranges.
+        var comments = [], comment, len, i, cursor;
+
+        if (!tree.range) {
+            throw new Error('attachComments needs range information');
+        }
+
+        // tokens array is empty, we attach comments to tree as 'leadingComments'
+        if (!tokens.length) {
+            if (providedComments.length) {
+                for (i = 0, len = providedComments.length; i < len; i += 1) {
+                    comment = deepCopy(providedComments[i]);
+                    comment.extendedRange = [0, tree.range[0]];
+                    comments.push(comment);
+                }
+                tree.leadingComments = comments;
+            }
+            return tree;
+        }
+
+        for (i = 0, len = providedComments.length; i < len; i += 1) {
+            comments.push(extendCommentRange(deepCopy(providedComments[i]), tokens));
+        }
+
+        // This is based on John Freeman's implementation.
+        cursor = 0;
+        traverse(tree, {
+            enter: function (node) {
+                var comment;
+
+                while (cursor < comments.length) {
+                    comment = comments[cursor];
+                    if (comment.extendedRange[1] > node.range[0]) {
+                        break;
+                    }
+
+                    if (comment.extendedRange[1] === node.range[0]) {
+                        if (!node.leadingComments) {
+                            node.leadingComments = [];
+                        }
+                        node.leadingComments.push(comment);
+                        comments.splice(cursor, 1);
+                    } else {
+                        cursor += 1;
+                    }
+                }
+
+                // already out of owned node
+                if (cursor === comments.length) {
+                    return VisitorOption.Break;
+                }
+
+                if (comments[cursor].extendedRange[0] > node.range[1]) {
+                    return VisitorOption.Skip;
+                }
+            }
+        });
+
+        cursor = 0;
+        traverse(tree, {
+            leave: function (node) {
+                var comment;
+
+                while (cursor < comments.length) {
+                    comment = comments[cursor];
+                    if (node.range[1] < comment.extendedRange[0]) {
+                        break;
+                    }
+
+                    if (node.range[1] === comment.extendedRange[0]) {
+                        if (!node.trailingComments) {
+                            node.trailingComments = [];
+                        }
+                        node.trailingComments.push(comment);
+                        comments.splice(cursor, 1);
+                    } else {
+                        cursor += 1;
+                    }
+                }
+
+                // already out of owned node
+                if (cursor === comments.length) {
+                    return VisitorOption.Break;
+                }
+
+                if (comments[cursor].extendedRange[0] > node.range[1]) {
+                    return VisitorOption.Skip;
+                }
+            }
+        });
+
+        return tree;
+    }
+
+    exports.version = '1.8.0';
+    exports.Syntax = Syntax;
+    exports.traverse = traverse;
+    exports.replace = replace;
+    exports.attachComments = attachComments;
+    exports.VisitorKeys = VisitorKeys;
+    exports.VisitorOption = VisitorOption;
+    exports.Controller = Controller;
+}));
+/* vim: set sw=4 ts=4 et tw=80 : */
diff --git a/packages/context-coloring/languages/javascript/scopifier.js b/packages/context-coloring/languages/javascript/scopifier.js
new file mode 100644
index 0000000..fd3549a
--- /dev/null
+++ b/packages/context-coloring/languages/javascript/scopifier.js
@@ -0,0 +1,129 @@
+// Copyright (C) 2014-2015  Free Software Foundation, Inc.
+
+// This file is part of GNU Emacs.
+
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+'use strict';
+
+var escope = require('./libraries/escope'),
+    esprima = require('./libraries/esprima');
+
+// Given code, returns an array of tokens for context-coloring.
+function scopifier(code) {
+
+    var analyzedScopes,
+        ast,
+        definition,
+        definitionsCount,
+        definitionsIndex,
+        i,
+        isDefined,
+        j,
+        k,
+        pointer,
+        range,
+        reference,
+        scope,
+        scopes,
+        tokens,
+        variable;
+
+    // Strip BOM.
+    code = code.replace(/^\ufeff/g, '');
+
+    // Gracefully handle parse errors by doing nothing.
+    try {
+        ast = esprima.parse(code, {
+            range: true
+        });
+        analyzedScopes = escope.analyze(ast).scopes;
+    } catch (error) {
+        process.exit(1);
+    }
+
+    scopes = [];
+    tokens = [];
+    for (i = 0; i < analyzedScopes.length; i += 1) {
+        scope = analyzedScopes[i];
+        // Having its level set implies it was already annotated.
+        if (scope.level === undefined) {
+            if (scope.upper) {
+                if (scope.upper.functionExpressionScope) {
+                    // Pretend function expression scope doesn't exist.
+                    scope.level = scope.upper.level;
+                    scope.variables = scope.upper.variables.concat(scope.variables);
+                } else {
+                    scope.level = scope.upper.level + 1;
+                }
+            } else {
+                // Base case.
+                scope.level = 0;
+            }
+            // We've only given the scope a level for posterity's sake.  We're
+            // done now.
+            if (!scope.functionExpressionScope) {
+                range = scope.block.range;
+                scopes.push(
+                    range[0] + 1,
+                    range[1] + 1,
+                    scope.level
+                );
+                definitionsIndex = tokens.length;
+                definitionsCount = 0;
+                for (j = 0; j < scope.variables.length; j += 1) {
+                    variable = scope.variables[j];
+                    definitionsCount += variable.defs.length;
+                    for (k = 0; k < variable.defs.length; k += 1) {
+                        definition = variable.defs[k];
+                        range = definition.name.range;
+                        tokens.push(
+                            range[0] + 1,
+                            range[1] + 1,
+                            scope.level
+                        );
+                    }
+                }
+                for (j = 0; j < scope.references.length; j += 1) {
+                    reference = scope.references[j];
+                    range = reference.identifier.range;
+                    isDefined = false;
+                    // Determine if a definition already exists for the range.
+                    // (escope detects variables twice if they are declared and
+                    // initialized simultaneously; this filters them.)
+                    for (k = 0; k < definitionsCount; k += 1) {
+                        pointer = definitionsIndex + (k * 3);
+                        if (tokens[pointer] === range[0] + 1 &&
+                                tokens[pointer + 1] === range[1] + 1) {
+                            isDefined = true;
+                            break;
+                        }
+                    }
+                    if (!isDefined) {
+                        tokens.push(
+                            // Handle global references too.
+                            range[0] + 1,
+                            range[1] + 1,
+                            reference.resolved ? reference.resolved.scope.level : 0
+                        );
+                    }
+                }
+            }
+        }
+    }
+
+    return scopes.concat(tokens);
+}
+
+module.exports = scopifier;
diff --git a/packages/context-coloring/libraries/.dir-locals.el b/packages/context-coloring/libraries/.dir-locals.el
new file mode 100644
index 0000000..d0b7cd5
--- /dev/null
+++ b/packages/context-coloring/libraries/.dir-locals.el
@@ -0,0 +1,2 @@
+((nil . ((sentence-end-double-space . t)
+         (buffer-read-only . t))))
diff --git a/packages/context-coloring/libraries/ert-async.el b/packages/context-coloring/libraries/ert-async.el
new file mode 100644
index 0000000..bcff3b0
--- /dev/null
+++ b/packages/context-coloring/libraries/ert-async.el
@@ -0,0 +1,89 @@
+;;; ert-async.el --- Async support for ERT -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2014 Johan Andersson
+
+;; Author: Johan Andersson <johan.rejeep@gmail.com>
+;; Maintainer: Johan Andersson <johan.rejeep@gmail.com>
+;; Version: 0.1.1
+;; Keywords: test
+;; URL: http://github.com/rejeep/ert-async.el
+
+;; This file is NOT part of GNU Emacs.
+
+;;; License:
+
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation; either version 3, or (at your option)
+;; any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs; see the file COPYING.  If not, write to the
+;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+;; Boston, MA 02110-1301, USA.
+
+;;; Commentary:
+
+;;; Code:
+
+(require 'ert)
+
+(defvar ert-async-timeout 10
+  "Number of seconds to wait for callbacks before failing.")
+
+(defun ert-async-activate-font-lock-keywords ()
+  "Activate font-lock keywords for `ert-deftest-async'."
+  (font-lock-add-keywords
+   nil
+   '(("(\\(\\<ert-deftest\\(?:-async\\)?\\)\\>\\s *\\(\\(?:\\sw\\|\\s_\\)+\\)?"
+      (1 font-lock-keyword-face nil t)
+      (2 font-lock-function-name-face nil t)))))
+
+(defmacro ert-deftest-async (name callbacks &rest body)
+  "Like `ert-deftest' but with support for async.
+
+NAME is the name of the test, which is the first argument to
+`ert-deftest'.
+
+CALLBACKS is a list of callback functions that all must be called
+before `ert-async-timeout'.  If all callback functions have not
+been called before the timeout, the test fails.
+
+The callback functions should be called without any argument.  If
+a callback function is called with a string as argument, the test
+will fail with that error string.
+
+BODY is the actual test."
+  (declare (indent 2))
+  (let ((varlist
+         (cons
+          'callbacked
+          (mapcar
+           (lambda (callback)
+             (list
+              callback
+              `(lambda (&optional error-message)
+                 (if error-message
+                     (ert-fail (format "Callback %s invoked with argument: %s" ',callback error-message))
+                   (if (member ',callback callbacked)
+                       (ert-fail (format "Callback %s called multiple times" ',callback))
+                     (push ',callback callbacked))))))
+           callbacks))))
+    `(ert-deftest ,name ()
+       (let* ,varlist
+         (with-timeout
+             (ert-async-timeout
+              (ert-fail (format "Timeout of %ds exceeded" ert-async-timeout)))
+           ,@body
+           (while (not (equal (sort (mapcar 'symbol-name callbacked) 'string<)
+                              (sort (mapcar 'symbol-name ',callbacks) 'string<)))
+             (accept-process-output nil 0.05)))))))
+
+(provide 'ert-async)
+
+;;; ert-async.el ends here
diff --git a/packages/context-coloring/libraries/js2-mode.el b/packages/context-coloring/libraries/js2-mode.el
new file mode 100644
index 0000000..c332cfc
--- /dev/null
+++ b/packages/context-coloring/libraries/js2-mode.el
@@ -0,0 +1,12331 @@
+;;; js2-mode.el --- Improved JavaScript editing mode
+
+;; Copyright (C) 2009, 2011-2014  Free Software Foundation, Inc.
+
+;; Author: Steve Yegge <steve.yegge@gmail.com>
+;;         mooz <stillpedant@gmail.com>
+;;         Dmitry Gutov <dgutov@yandex.ru>
+;; URL:  https://github.com/mooz/js2-mode/
+;;       http://code.google.com/p/js2-mode/
+;; Version: 20141118
+;; Keywords: languages, javascript
+;; Package-Requires: ((emacs "24.1") (cl-lib "0.5"))
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; This JavaScript editing mode supports:
+
+;;  - strict recognition of the Ecma-262 language standard
+;;  - support for most Rhino and SpiderMonkey extensions from 1.5 and up
+;;  - parsing support for ECMAScript for XML (E4X, ECMA-357)
+;;  - accurate syntax highlighting using a recursive-descent parser
+;;  - on-the-fly reporting of syntax errors and strict-mode warnings
+;;  - undeclared-variable warnings using a configurable externs framework
+;;  - "bouncing" line indentation to choose among alternate indentation points
+;;  - smart line-wrapping within comments and strings
+;;  - code folding:
+;;    - show some or all function bodies as {...}
+;;    - show some or all block comments as /*...*/
+;;  - context-sensitive menu bar and popup menus
+;;  - code browsing using the `imenu' package
+;;  - many customization options
+
+;; Installation:
+;;
+;; To install it as your major mode for JavaScript editing:
+
+;;   (add-to-list 'auto-mode-alist '("\\.js\\'" . js2-mode))
+
+;; Alternatively, to install it as a minor mode just for JavaScript linting,
+;; you must add it to the appropriate major-mode hook.  Normally this would be:
+
+;;   (add-hook 'js-mode-hook 'js2-minor-mode)
+
+;; You may also want to hook it in for shell scripts running via node.js:
+
+;;   (add-to-list 'interpreter-mode-alist '("node" . js2-mode))
+
+;; To customize how it works:
+;;   M-x customize-group RET js2-mode RET
+
+;; Notes:
+
+;; This mode includes a port of Mozilla Rhino's scanner, parser and
+;; symbol table.  Ideally it should stay in sync with Rhino, keeping
+;; `js2-mode' current as the EcmaScript language standard evolves.
+
+;; Unlike cc-engine based language modes, js2-mode's line-indentation is not
+;; customizable.  It is a surprising amount of work to support customizable
+;; indentation.  The current compromise is that the tab key lets you cycle among
+;; various likely indentation points, similar to the behavior of python-mode.
+
+;; This mode does not yet work with "multi-mode" modes such as `mmm-mode'
+;; and `mumamo', although it could be made to do so with some effort.
+;; This means that `js2-mode' is currently only useful for editing JavaScript
+;; files, and not for editing JavaScript within <script> tags or templates.
+
+;; The project page on GitHub is used for development and issue tracking.
+;; The original homepage at Google Code has outdated information and is mostly
+;; unmaintained.
+
+;;; Code:
+
+(require 'cl-lib)
+(require 'imenu)
+(require 'cc-cmds)  ; for `c-fill-paragraph'
+
+(eval-and-compile
+  (require 'cc-mode)     ; (only) for `c-populate-syntax-table'
+  (require 'cc-engine))  ; for `c-paragraph-start' et. al.
+
+(defvar electric-layout-rules)
+
+;;; Externs (variables presumed to be defined by the host system)
+
+(defvar js2-ecma-262-externs
+  (mapcar 'symbol-name
+          '(Array Boolean Date Error EvalError Function Infinity JSON
+          Math NaN Number Object RangeError ReferenceError RegExp
+          String SyntaxError TypeError URIError
+          decodeURI decodeURIComponent encodeURI
+          encodeURIComponent escape eval isFinite isNaN
+          parseFloat parseInt undefined unescape))
+"Ecma-262 externs.  Included in `js2-externs' by default.")
+
+(defvar js2-browser-externs
+  (mapcar 'symbol-name
+          '(;; DOM level 1
+            Attr CDATASection CharacterData Comment DOMException
+            DOMImplementation Document DocumentFragment
+            DocumentType Element Entity EntityReference
+            ExceptionCode NamedNodeMap Node NodeList Notation
+            ProcessingInstruction Text
+
+            ;; DOM level 2
+            HTMLAnchorElement HTMLAppletElement HTMLAreaElement
+            HTMLBRElement HTMLBaseElement HTMLBaseFontElement
+            HTMLBodyElement HTMLButtonElement HTMLCollection
+            HTMLDListElement HTMLDirectoryElement HTMLDivElement
+            HTMLDocument HTMLElement HTMLFieldSetElement
+            HTMLFontElement HTMLFormElement HTMLFrameElement
+            HTMLFrameSetElement HTMLHRElement HTMLHeadElement
+            HTMLHeadingElement HTMLHtmlElement HTMLIFrameElement
+            HTMLImageElement HTMLInputElement HTMLIsIndexElement
+            HTMLLIElement HTMLLabelElement HTMLLegendElement
+            HTMLLinkElement HTMLMapElement HTMLMenuElement
+            HTMLMetaElement HTMLModElement HTMLOListElement
+            HTMLObjectElement HTMLOptGroupElement
+            HTMLOptionElement HTMLOptionsCollection
+            HTMLParagraphElement HTMLParamElement HTMLPreElement
+            HTMLQuoteElement HTMLScriptElement HTMLSelectElement
+            HTMLStyleElement HTMLTableCaptionElement
+            HTMLTableCellElement HTMLTableColElement
+            HTMLTableElement HTMLTableRowElement
+            HTMLTableSectionElement HTMLTextAreaElement
+            HTMLTitleElement HTMLUListElement
+
+            ;; DOM level 3
+            DOMConfiguration DOMError DOMException
+            DOMImplementationList DOMImplementationSource
+            DOMLocator DOMStringList NameList TypeInfo
+            UserDataHandler
+
+            ;; Window
+            window alert confirm document java navigator prompt screen
+            self top
+
+            ;; W3C CSS
+            CSSCharsetRule CSSFontFace CSSFontFaceRule
+            CSSImportRule CSSMediaRule CSSPageRule
+            CSSPrimitiveValue CSSProperties CSSRule CSSRuleList
+            CSSStyleDeclaration CSSStyleRule CSSStyleSheet
+            CSSValue CSSValueList Counter DOMImplementationCSS
+            DocumentCSS DocumentStyle ElementCSSInlineStyle
+            LinkStyle MediaList RGBColor Rect StyleSheet
+            StyleSheetList ViewCSS
+
+            ;; W3C Event
+            EventListener EventTarget Event DocumentEvent UIEvent
+            MouseEvent MutationEvent KeyboardEvent
+
+            ;; W3C Range
+            DocumentRange Range RangeException
+
+            ;; W3C XML
+            XPathResult XMLHttpRequest
+
+            ;; console object.  Provided by at least Chrome and Firefox.
+            console))
+  "Browser externs.
+You can cause these to be included or excluded with the custom
+variable `js2-include-browser-externs'.")
+
+(defvar js2-rhino-externs
+  (mapcar 'symbol-name
+          '(Packages importClass importPackage com org java
+            ;; Global object (shell) externs.
+            defineClass deserialize doctest gc help load
+            loadClass print quit readFile readUrl runCommand seal
+            serialize spawn sync toint32 version))
+  "Mozilla Rhino externs.
+Set `js2-include-rhino-externs' to t to include them.")
+
+(defvar js2-node-externs
+  (mapcar 'symbol-name
+          '(__dirname __filename Buffer clearInterval clearTimeout require
+            console exports global module process setInterval setTimeout))
+  "Node.js externs.
+Set `js2-include-node-externs' to t to include them.")
+
+(defvar js2-typed-array-externs
+  (mapcar 'symbol-name
+          '(ArrayBuffer Uint8ClampedArray DataView
+            Int8Array Uint8Array Int16Array Uint16Array Int32Array Uint32Array
+            Float32Array Float64Array))
+  "Khronos typed array externs. Available in most modern browsers and
+in node.js >= 0.6. If `js2-include-node-externs' or `js2-include-browser-externs'
+are enabled, these will also be included.")
+
+(defvar js2-harmony-externs
+  (mapcar 'symbol-name
+          '(Map Promise Proxy Reflect Set Symbol WeakMap WeakSet))
+  "ES6 externs.  If `js2-include-browser-externs' is enabled and
+`js2-language-version' is sufficiently high, these will be included.")
+
+;;; Variables
+
+(defun js2-mark-safe-local (name pred)
+  "Make the variable NAME buffer-local and mark it as safe file-local
+variable with predicate PRED."
+  (make-variable-buffer-local name)
+  (put name 'safe-local-variable pred))
+
+(defcustom js2-highlight-level 2
+  "Amount of syntax highlighting to perform.
+0 or a negative value means none.
+1 adds basic syntax highlighting.
+2 adds highlighting of some Ecma built-in properties.
+3 adds highlighting of many Ecma built-in functions."
+  :group 'js2-mode
+  :type '(choice (const :tag "None" 0)
+                 (const :tag "Basic" 1)
+                 (const :tag "Include Properties" 2)
+                 (const :tag "Include Functions" 3)))
+
+(defvar js2-mode-dev-mode-p nil
+  "Non-nil if running in development mode.  Normally nil.")
+
+(defgroup js2-mode nil
+  "An improved JavaScript mode."
+  :group 'languages)
+
+(defcustom js2-basic-offset (if (and (boundp 'c-basic-offset)
+                                     (numberp c-basic-offset))
+                                c-basic-offset
+                              4)
+  "Number of spaces to indent nested statements.
+Similar to `c-basic-offset'."
+  :group 'js2-mode
+  :type 'integer)
+(js2-mark-safe-local 'js2-basic-offset 'integerp)
+
+(defcustom js2-bounce-indent-p nil
+  "Non-nil to have indent-line function choose among alternatives.
+If nil, the indent-line function will indent to a predetermined column
+based on heuristic guessing.  If non-nil, then if the current line is
+already indented to that predetermined column, indenting will choose
+another likely column and indent to that spot.  Repeated invocation of
+the indent-line function will cycle among the computed alternatives.
+See the function `js2-bounce-indent' for details.  When it is non-nil,
+js2-mode also binds `js2-bounce-indent-backwards' to Shift-Tab."
+  :type 'boolean
+  :group 'js2-mode)
+
+(defcustom js2-pretty-multiline-declarations t
+  "Non-nil to line up multiline declarations vertically:
+
+  var a = 10,
+      b = 20,
+      c = 30;
+
+If the value is not `all', and the first assigned value in
+declaration is a function/array/object literal spanning several
+lines, it won't be indented additionally:
+
+  var o = {                   var bar = 2,
+    foo: 3          vs.           o = {
+  },                                foo: 3
+      bar = 2;                    };"
+  :group 'js2-mode
+  :type 'symbol)
+(js2-mark-safe-local 'js2-pretty-multiline-declarations 'symbolp)
+
+(defcustom js2-indent-switch-body nil
+  "When nil, case labels are indented on the same level as the
+containing switch statement.  Otherwise, all lines inside
+switch statement body are indented one additional level."
+  :type 'boolean
+  :group 'js2-mode)
+(js2-mark-safe-local 'js2-indent-case-same-as-switch 'booleanp)
+
+(defcustom js2-idle-timer-delay 0.2
+  "Delay in secs before re-parsing after user makes changes.
+Multiplied by `js2-dynamic-idle-timer-adjust', which see."
+  :type 'number
+  :group 'js2-mode)
+(make-variable-buffer-local 'js2-idle-timer-delay)
+
+(defcustom js2-dynamic-idle-timer-adjust 0
+  "Positive to adjust `js2-idle-timer-delay' based on file size.
+The idea is that for short files, parsing is faster so we can be
+more responsive to user edits without interfering with editing.
+The buffer length in characters (typically bytes) is divided by
+this value and used to multiply `js2-idle-timer-delay' for the
+buffer.  For example, a 21k file and 10k adjust yields 21k/10k
+== 2, so js2-idle-timer-delay is multiplied by 2.
+If `js2-dynamic-idle-timer-adjust' is 0 or negative,
+`js2-idle-timer-delay' is not dependent on the file size."
+  :type 'number
+  :group 'js2-mode)
+
+(defcustom js2-concat-multiline-strings t
+  "When non-nil, `js2-line-break' in mid-string will make it a
+string concatenation. When `eol', the '+' will be inserted at the
+end of the line, otherwise, at the beginning of the next line."
+  :type '(choice (const t) (const eol) (const nil))
+  :group 'js2-mode)
+
+(defcustom js2-mode-show-parse-errors t
+  "True to highlight parse errors."
+  :type 'boolean
+  :group 'js2-mode)
+
+(defcustom js2-mode-show-strict-warnings t
+  "Non-nil to emit Ecma strict-mode warnings.
+Some of the warnings can be individually disabled by other flags,
+even if this flag is non-nil."
+  :type 'boolean
+  :group 'js2-mode)
+
+(defcustom js2-strict-trailing-comma-warning t
+  "Non-nil to warn about trailing commas in array literals.
+Ecma-262-5.1 allows them, but older versions of IE raise an error."
+  :type 'boolean
+  :group 'js2-mode)
+
+(defcustom js2-strict-missing-semi-warning t
+  "Non-nil to warn about semicolon auto-insertion after statement.
+Technically this is legal per Ecma-262, but some style guides disallow
+depending on it."
+  :type 'boolean
+  :group 'js2-mode)
+
+(defcustom js2-missing-semi-one-line-override nil
+  "Non-nil to permit missing semicolons in one-line functions.
+In one-liner functions such as `function identity(x) {return x}'
+people often omit the semicolon for a cleaner look.  If you are
+such a person, you can suppress the missing-semicolon warning
+by setting this variable to t."
+  :type 'boolean
+  :group 'js2-mode)
+
+(defcustom js2-strict-inconsistent-return-warning t
+  "Non-nil to warn about mixing returns with value-returns.
+It's perfectly legal to have a `return' and a `return foo' in the
+same function, but it's often an indicator of a bug, and it also
+interferes with type inference (in systems that support it.)"
+  :type 'boolean
+  :group 'js2-mode)
+
+(defcustom js2-strict-cond-assign-warning t
+  "Non-nil to warn about expressions like if (a = b).
+This often should have been '==' instead of '='.  If the warning
+is enabled, you can suppress it on a per-expression basis by
+parenthesizing the expression, e.g. if ((a = b)) ..."
+  :type 'boolean
+  :group 'js2-mode)
+
+(defcustom js2-strict-var-redeclaration-warning t
+  "Non-nil to warn about redeclaring variables in a script or function."
+  :type 'boolean
+  :group 'js2-mode)
+
+(defcustom js2-strict-var-hides-function-arg-warning t
+  "Non-nil to warn about a var decl hiding a function argument."
+  :type 'boolean
+  :group 'js2-mode)
+
+(defcustom js2-skip-preprocessor-directives nil
+  "Non-nil to treat lines beginning with # as comments.
+Useful for viewing Mozilla JavaScript source code."
+  :type 'boolean
+  :group 'js2-mode)
+
+(defcustom js2-language-version 200
+  "Configures what JavaScript language version to recognize.
+Currently versions 150, 160, 170, 180 and 200 are supported,
+corresponding to JavaScript 1.5, 1.6, 1.7, 1.8 and 2.0 (Harmony),
+respectively.  In a nutshell, 1.6 adds E4X support, 1.7 adds let,
+yield, and Array comprehensions, and 1.8 adds function closures."
+  :type 'integer
+  :group 'js2-mode)
+
+(defcustom js2-instanceof-has-side-effects nil
+  "If non-nil, treats the instanceof operator as having side effects.
+This is useful for xulrunner apps."
+  :type 'boolean
+  :group 'js2-mode)
+
+(defcustom js2-move-point-on-right-click t
+  "Non-nil to move insertion point when you right-click.
+This makes right-click context menu behavior a bit more intuitive,
+since menu operations generally apply to the point.  The exception
+is if there is a region selection, in which case the point does -not-
+move, so cut/copy/paste can work properly.
+
+Note that IntelliJ moves the point, and Eclipse leaves it alone,
+so this behavior is customizable."
+  :group 'js2-mode
+  :type 'boolean)
+
+(defcustom js2-allow-rhino-new-expr-initializer t
+  "Non-nil to support a Rhino's experimental syntactic construct.
+
+Rhino supports the ability to follow a `new' expression with an object
+literal, which is used to set additional properties on the new object
+after calling its constructor.  Syntax:
+
+  new <expr> [ ( arglist ) ] [initializer]
+
+Hence, this expression:
+
+  new Object {a: 1, b: 2}
+
+results in an Object with properties a=1 and b=2.  This syntax is
+apparently not configurable in Rhino - it's currently always enabled,
+as of Rhino version 1.7R2."
+  :type 'boolean
+  :group 'js2-mode)
+
+(defcustom js2-allow-member-expr-as-function-name nil
+  "Non-nil to support experimental Rhino syntax for function names.
+
+Rhino supports an experimental syntax configured via the Rhino Context
+setting `allowMemberExprAsFunctionName'.  The experimental syntax is:
+
+  function <member-expr> ( [ arg-list ] ) { <body> }
+
+Where member-expr is a non-parenthesized 'member expression', which
+is anything at the grammar level of a new-expression or lower, meaning
+any expression that does not involve infix or unary operators.
+
+When <member-expr> is not a simple identifier, then it is syntactic
+sugar for assigning the anonymous function to the <member-expr>.  Hence,
+this code:
+
+  function a.b().c[2] (x, y) { ... }
+
+is rewritten as:
+
+  a.b().c[2] = function(x, y) {...}
+
+which doesn't seem particularly useful, but Rhino permits it."
+  :type 'boolean
+  :group 'js2-mode)
+
+;; scanner variables
+
+(defmacro js2-deflocal (name value &optional comment)
+  "Define a buffer-local variable NAME with VALUE and COMMENT."
+  (declare (debug defvar) (doc-string 3))
+  `(progn
+     (defvar ,name ,value ,comment)
+     (make-variable-buffer-local ',name)))
+
+(defvar js2-EOF_CHAR -1
+  "Represents end of stream.  Distinct from js2-EOF token type.")
+
+;; I originally used symbols to represent tokens, but Rhino uses
+;; ints and then sets various flag bits in them, so ints it is.
+;; The upshot is that we need a `js2-' prefix in front of each name.
+(defvar js2-ERROR -1)
+(defvar js2-EOF 0)
+(defvar js2-EOL 1)
+(defvar js2-ENTERWITH 2)       ; begin interpreter bytecodes
+(defvar js2-LEAVEWITH 3)
+(defvar js2-RETURN 4)
+(defvar js2-GOTO 5)
+(defvar js2-IFEQ 6)
+(defvar js2-IFNE 7)
+(defvar js2-SETNAME 8)
+(defvar js2-BITOR 9)
+(defvar js2-BITXOR 10)
+(defvar js2-BITAND 11)
+(defvar js2-EQ 12)
+(defvar js2-NE 13)
+(defvar js2-LT 14)
+(defvar js2-LE 15)
+(defvar js2-GT 16)
+(defvar js2-GE 17)
+(defvar js2-LSH 18)
+(defvar js2-RSH 19)
+(defvar js2-URSH 20)
+(defvar js2-ADD 21)            ; infix plus
+(defvar js2-SUB 22)            ; infix minus
+(defvar js2-MUL 23)
+(defvar js2-DIV 24)
+(defvar js2-MOD 25)
+(defvar js2-NOT 26)
+(defvar js2-BITNOT 27)
+(defvar js2-POS 28)            ; unary plus
+(defvar js2-NEG 29)            ; unary minus
+(defvar js2-NEW 30)
+(defvar js2-DELPROP 31)
+(defvar js2-TYPEOF 32)
+(defvar js2-GETPROP 33)
+(defvar js2-GETPROPNOWARN 34)
+(defvar js2-SETPROP 35)
+(defvar js2-GETELEM 36)
+(defvar js2-SETELEM 37)
+(defvar js2-CALL 38)
+(defvar js2-NAME 39)           ; an identifier
+(defvar js2-NUMBER 40)
+(defvar js2-STRING 41)
+(defvar js2-NULL 42)
+(defvar js2-THIS 43)
+(defvar js2-FALSE 44)
+(defvar js2-TRUE 45)
+(defvar js2-SHEQ 46)           ; shallow equality (===)
+(defvar js2-SHNE 47)           ; shallow inequality (!==)
+(defvar js2-REGEXP 48)
+(defvar js2-BINDNAME 49)
+(defvar js2-THROW 50)
+(defvar js2-RETHROW 51)        ; rethrow caught exception: catch (e if ) uses it
+(defvar js2-IN 52)
+(defvar js2-INSTANCEOF 53)
+(defvar js2-LOCAL_LOAD 54)
+(defvar js2-GETVAR 55)
+(defvar js2-SETVAR 56)
+(defvar js2-CATCH_SCOPE 57)
+(defvar js2-ENUM_INIT_KEYS 58) ; FIXME: what are these?
+(defvar js2-ENUM_INIT_VALUES 59)
+(defvar js2-ENUM_INIT_ARRAY 60)
+(defvar js2-ENUM_NEXT 61)
+(defvar js2-ENUM_ID 62)
+(defvar js2-THISFN 63)
+(defvar js2-RETURN_RESULT 64)  ; to return previously stored return result
+(defvar js2-ARRAYLIT 65)       ; array literal
+(defvar js2-OBJECTLIT 66)      ; object literal
+(defvar js2-GET_REF 67)        ; *reference
+(defvar js2-SET_REF 68)        ; *reference = something
+(defvar js2-DEL_REF 69)        ; delete reference
+(defvar js2-REF_CALL 70)       ; f(args) = something or f(args)++
+(defvar js2-REF_SPECIAL 71)    ; reference for special properties like __proto
+(defvar js2-YIELD 72)          ; JS 1.7 yield pseudo keyword
+
+;; XML support
+(defvar js2-DEFAULTNAMESPACE 73)
+(defvar js2-ESCXMLATTR 74)
+(defvar js2-ESCXMLTEXT 75)
+(defvar js2-REF_MEMBER 76)     ; Reference for x.@y, x..y etc.
+(defvar js2-REF_NS_MEMBER 77)  ; Reference for x.ns::y, x..ns::y etc.
+(defvar js2-REF_NAME 78)       ; Reference for @y, @[y] etc.
+(defvar js2-REF_NS_NAME 79)    ; Reference for ns::y, @ns::y@[y] etc.
+
+(defvar js2-first-bytecode js2-ENTERWITH)
+(defvar js2-last-bytecode js2-REF_NS_NAME)
+
+(defvar js2-TRY 80)
+(defvar js2-SEMI 81)           ; semicolon
+(defvar js2-LB 82)             ; left and right brackets
+(defvar js2-RB 83)
+(defvar js2-LC 84)             ; left and right curly-braces
+(defvar js2-RC 85)
+(defvar js2-LP 86)             ; left and right parens
+(defvar js2-RP 87)
+(defvar js2-COMMA 88)          ; comma operator
+
+(defvar js2-ASSIGN 89)         ; simple assignment (=)
+(defvar js2-ASSIGN_BITOR 90)   ; |=
+(defvar js2-ASSIGN_BITXOR 91)  ; ^=
+(defvar js2-ASSIGN_BITAND 92)  ; &=
+(defvar js2-ASSIGN_LSH 93)     ; <<=
+(defvar js2-ASSIGN_RSH 94)     ; >>=
+(defvar js2-ASSIGN_URSH 95)    ; >>>=
+(defvar js2-ASSIGN_ADD 96)     ; +=
+(defvar js2-ASSIGN_SUB 97)     ; -=
+(defvar js2-ASSIGN_MUL 98)     ; *=
+(defvar js2-ASSIGN_DIV 99)     ; /=
+(defvar js2-ASSIGN_MOD 100)    ; %=
+
+(defvar js2-first-assign js2-ASSIGN)
+(defvar js2-last-assign js2-ASSIGN_MOD)
+
+(defvar js2-HOOK 101)          ; conditional (?:)
+(defvar js2-COLON 102)
+(defvar js2-OR 103)            ; logical or (||)
+(defvar js2-AND 104)           ; logical and (&&)
+(defvar js2-INC 105)           ; increment/decrement (++ --)
+(defvar js2-DEC 106)
+(defvar js2-DOT 107)           ; member operator (.)
+(defvar js2-FUNCTION 108)      ; function keyword
+(defvar js2-EXPORT 109)        ; export keyword
+(defvar js2-IMPORT 110)        ; import keyword
+(defvar js2-IF 111)            ; if keyword
+(defvar js2-ELSE 112)          ; else keyword
+(defvar js2-SWITCH 113)        ; switch keyword
+(defvar js2-CASE 114)          ; case keyword
+(defvar js2-DEFAULT 115)       ; default keyword
+(defvar js2-WHILE 116)         ; while keyword
+(defvar js2-DO 117)            ; do keyword
+(defvar js2-FOR 118)           ; for keyword
+(defvar js2-BREAK 119)         ; break keyword
+(defvar js2-CONTINUE 120)      ; continue keyword
+(defvar js2-VAR 121)           ; var keyword
+(defvar js2-WITH 122)          ; with keyword
+(defvar js2-CATCH 123)         ; catch keyword
+(defvar js2-FINALLY 124)       ; finally keyword
+(defvar js2-VOID 125)          ; void keyword
+(defvar js2-RESERVED 126)      ; reserved keywords
+
+(defvar js2-EMPTY 127)
+
+;; Types used for the parse tree - never returned by scanner.
+
+(defvar js2-BLOCK 128)         ; statement block
+(defvar js2-LABEL 129)         ; label
+(defvar js2-TARGET 130)
+(defvar js2-LOOP 131)
+(defvar js2-EXPR_VOID 132)     ; expression statement in functions
+(defvar js2-EXPR_RESULT 133)   ; expression statement in scripts
+(defvar js2-JSR 134)
+(defvar js2-SCRIPT 135)        ; top-level node for entire script
+(defvar js2-TYPEOFNAME 136)    ; for typeof(simple-name)
+(defvar js2-USE_STACK 137)
+(defvar js2-SETPROP_OP 138)    ; x.y op= something
+(defvar js2-SETELEM_OP 139)    ; x[y] op= something
+(defvar js2-LOCAL_BLOCK 140)
+(defvar js2-SET_REF_OP 141)    ; *reference op= something
+
+;; For XML support:
+(defvar js2-DOTDOT 142)        ; member operator (..)
+(defvar js2-COLONCOLON 143)    ; namespace::name
+(defvar js2-XML 144)           ; XML type
+(defvar js2-DOTQUERY 145)      ; .() -- e.g., x.emps.emp.(name == "terry")
+(defvar js2-XMLATTR 146)       ; @
+(defvar js2-XMLEND 147)
+
+;; Optimizer-only tokens
+(defvar js2-TO_OBJECT 148)
+(defvar js2-TO_DOUBLE 149)
+
+(defvar js2-GET 150)           ; JS 1.5 get pseudo keyword
+(defvar js2-SET 151)           ; JS 1.5 set pseudo keyword
+(defvar js2-LET 152)           ; JS 1.7 let pseudo keyword
+(defvar js2-CONST 153)
+(defvar js2-SETCONST 154)
+(defvar js2-SETCONSTVAR 155)
+(defvar js2-ARRAYCOMP 156)
+(defvar js2-LETEXPR 157)
+(defvar js2-WITHEXPR 158)
+(defvar js2-DEBUGGER 159)
+
+(defvar js2-COMMENT 160)
+(defvar js2-TRIPLEDOT 161)     ; for rest parameter
+(defvar js2-ARROW 162)         ; function arrow (=>)
+(defvar js2-CLASS 163)
+(defvar js2-EXTENDS 164)
+(defvar js2-STATIC 165)
+(defvar js2-SUPER 166)
+(defvar js2-TEMPLATE_HEAD 167)    ; part of template literal before substitution
+(defvar js2-NO_SUBS_TEMPLATE 168) ; template literal without substitutions
+(defvar js2-TAGGED_TEMPLATE 169)  ; tagged template literal
+
+(defconst js2-num-tokens (1+ js2-TAGGED_TEMPLATE))
+
+(defconst js2-debug-print-trees nil)
+
+;; Rhino accepts any string or stream as input.  Emacs character
+;; processing works best in buffers, so we'll assume the input is a
+;; buffer.  JavaScript strings can be copied into temp buffers before
+;; scanning them.
+
+;; Buffer-local variables yield much cleaner code than using `defstruct'.
+;; They're the Emacs equivalent of instance variables, more or less.
+
+(js2-deflocal js2-ts-dirty-line nil
+  "Token stream buffer-local variable.
+Indicates stuff other than whitespace since start of line.")
+
+(js2-deflocal js2-ts-hit-eof nil
+  "Token stream buffer-local variable.")
+
+;; FIXME: Unused.
+(js2-deflocal js2-ts-line-start 0
+  "Token stream buffer-local variable.")
+
+(js2-deflocal js2-ts-lineno 1
+  "Token stream buffer-local variable.")
+
+;; FIXME: Unused.
+(js2-deflocal js2-ts-line-end-char -1
+  "Token stream buffer-local variable.")
+
+(js2-deflocal js2-ts-cursor 1  ; emacs buffers are 1-indexed
+  "Token stream buffer-local variable.
+Current scan position.")
+
+;; FIXME: Unused.
+(js2-deflocal js2-ts-is-xml-attribute nil
+  "Token stream buffer-local variable.")
+
+(js2-deflocal js2-ts-xml-is-tag-content nil
+  "Token stream buffer-local variable.")
+
+(js2-deflocal js2-ts-xml-open-tags-count 0
+  "Token stream buffer-local variable.")
+
+(js2-deflocal js2-ts-string-buffer nil
+  "Token stream buffer-local variable.
+List of chars built up while scanning various tokens.")
+
+(cl-defstruct (js2-token
+               (:constructor nil)
+               (:constructor make-js2-token (beg)))
+  "Value returned from the token stream."
+  (type js2-EOF)
+  (beg 1)
+  (end -1)
+  (string "")
+  number
+  regexp-flags
+  comment-type
+  follows-eol-p)
+
+;; Have to call `js2-init-scanner' to initialize the values.
+(js2-deflocal js2-ti-tokens nil)
+(js2-deflocal js2-ti-tokens-cursor nil)
+(js2-deflocal js2-ti-lookahead nil)
+
+(cl-defstruct (js2-ts-state
+               (:constructor make-js2-ts-state (&key (lineno js2-ts-lineno)
+                                                     (cursor js2-ts-cursor)
+                                                     (tokens (copy-sequence js2-ti-tokens))
+                                                     (tokens-cursor js2-ti-tokens-cursor)
+                                                     (lookahead js2-ti-lookahead))))
+  lineno
+  cursor
+  tokens
+  tokens-cursor
+  lookahead)
+
+;;; Parser variables
+
+(js2-deflocal js2-parsed-errors nil
+  "List of errors produced during scanning/parsing.")
+
+(js2-deflocal js2-parsed-warnings nil
+  "List of warnings produced during scanning/parsing.")
+
+(js2-deflocal js2-recover-from-parse-errors t
+  "Non-nil to continue parsing after a syntax error.
+
+In recovery mode, the AST will be built in full, and any error
+nodes will be flagged with appropriate error information.  If
+this flag is nil, a syntax error will result in an error being
+signaled.
+
+The variable is automatically buffer-local, because different
+modes that use the parser will need different settings.")
+
+(js2-deflocal js2-parse-hook nil
+  "List of callbacks for receiving parsing progress.")
+
+(defvar js2-parse-finished-hook nil
+  "List of callbacks to notify when parsing finishes.
+Not called if parsing was interrupted.")
+
+(js2-deflocal js2-is-eval-code nil
+  "True if we're evaluating code in a string.
+If non-nil, the tokenizer will record the token text, and the AST nodes
+will record their source text.  Off by default for IDE modes, since the
+text is available in the buffer.")
+
+(defvar js2-parse-ide-mode t
+  "Non-nil if the parser is being used for `js2-mode'.
+If non-nil, the parser will set text properties for fontification
+and the syntax table.  The value should be nil when using the
+parser as a frontend to an interpreter or byte compiler.")
+
+;;; Parser instance variables (buffer-local vars for js2-parse)
+
+(defconst js2-ti-after-eol (lsh 1 16)
+  "Flag:  first token of the source line.")
+
+;; Inline Rhino's CompilerEnvirons vars as buffer-locals.
+
+(js2-deflocal js2-compiler-generate-debug-info t)
+(js2-deflocal js2-compiler-use-dynamic-scope nil)
+(js2-deflocal js2-compiler-reserved-keywords-as-identifier nil)
+(js2-deflocal js2-compiler-xml-available t)
+(js2-deflocal js2-compiler-optimization-level 0)
+(js2-deflocal js2-compiler-generating-source t)
+(js2-deflocal js2-compiler-strict-mode nil)
+(js2-deflocal js2-compiler-report-warning-as-error nil)
+(js2-deflocal js2-compiler-generate-observer-count nil)
+(js2-deflocal js2-compiler-activation-names nil)
+
+;; SKIP:  sourceURI
+
+;; There's a compileFunction method in Context.java - may need it.
+(js2-deflocal js2-called-by-compile-function nil
+  "True if `js2-parse' was called by `js2-compile-function'.
+Will only be used when we finish implementing the interpreter.")
+
+;; SKIP:  ts  (we just call `js2-init-scanner' and use its vars)
+
+;; SKIP:  node factory - we're going to just call functions directly,
+;; and eventually go to a unified AST format.
+
+(js2-deflocal js2-nesting-of-function 0)
+
+(js2-deflocal js2-recorded-identifiers nil
+  "Tracks identifiers found during parsing.")
+
+(js2-deflocal js2-is-in-destructuring nil
+  "True while parsing destructuring expression.")
+
+(defcustom js2-global-externs nil
+  "A list of any extern names you'd like to consider always declared.
+This list is global and is used by all `js2-mode' files.
+You can create buffer-local externs list using `js2-additional-externs'.
+
+There is also a buffer-local variable `js2-default-externs',
+which is initialized by default to include the Ecma-262 externs
+and the standard browser externs.  The three lists are all
+checked during highlighting."
+  :type 'list
+  :group 'js2-mode)
+
+(js2-deflocal js2-default-externs nil
+  "Default external declarations.
+
+These are currently only used for highlighting undeclared variables,
+which only worries about top-level (unqualified) references.
+As js2-mode's processing improves, we will flesh out this list.
+
+The initial value is set to `js2-ecma-262-externs', unless some
+of the `js2-include-?-externs' variables are set to t, in which
+case the browser, Rhino and/or Node.js externs are also included.
+
+See `js2-additional-externs' for more information.")
+
+(defcustom js2-include-browser-externs t
+  "Non-nil to include browser externs in the master externs list.
+If you work on JavaScript files that are not intended for browsers,
+such as Mozilla Rhino server-side JavaScript, set this to nil.
+See `js2-additional-externs' for more information about externs."
+  :type 'boolean
+  :group 'js2-mode)
+
+(defcustom js2-include-rhino-externs nil
+  "Non-nil to include Mozilla Rhino externs in the master externs list.
+See `js2-additional-externs' for more information about externs."
+  :type 'boolean
+  :group 'js2-mode)
+
+(defcustom js2-include-node-externs nil
+  "Non-nil to include Node.js externs in the master externs list.
+See `js2-additional-externs' for more information about externs."
+  :type 'boolean
+  :group 'js2-mode)
+
+(js2-deflocal js2-additional-externs nil
+  "A buffer-local list of additional external declarations.
+It is used to decide whether variables are considered undeclared
+for purposes of highlighting.
+
+Each entry is a Lisp string.  The string should be the fully qualified
+name of an external entity.  All externs should be added to this list,
+so that as js2-mode's processing improves it can take advantage of them.
+
+You may want to declare your externs in three ways.
+First, you can add externs that are valid for all your JavaScript files.
+You should probably do this by adding them to `js2-global-externs', which
+is a global list used for all js2-mode files.
+
+Next, you can add a function to `js2-init-hook' that adds additional
+externs appropriate for the specific file, perhaps based on its path.
+These should go in `js2-additional-externs', which is buffer-local.
+
+Third, you can use JSLint's global declaration, as long as
+`js2-include-jslint-globals' is non-nil, which see.
+
+Finally, you can add a function to `js2-post-parse-callbacks',
+which is called after parsing completes, and `js2-mode-ast' is bound to
+the root of the parse tree.  At this stage you can set up an AST
+node visitor using `js2-visit-ast' and examine the parse tree
+for specific import patterns that may imply the existence of
+other externs, possibly tied to your build system.  These should also
+be added to `js2-additional-externs'.
+
+Your post-parse callback may of course also use the simpler and
+faster (but perhaps less robust) approach of simply scanning the
+buffer text for your imports, using regular expressions.")
+
+;; SKIP:  decompiler
+;; SKIP:  encoded-source
+
+;;; The following variables are per-function and should be saved/restored
+;;; during function parsing...
+
+(js2-deflocal js2-current-script-or-fn nil)
+(js2-deflocal js2-current-scope nil)
+(js2-deflocal js2-nesting-of-with 0)
+(js2-deflocal js2-label-set nil
+  "An alist mapping label names to nodes.")
+
+(js2-deflocal js2-loop-set nil)
+(js2-deflocal js2-loop-and-switch-set nil)
+(js2-deflocal js2-has-return-value nil)
+(js2-deflocal js2-end-flags 0)
+
+;;; ...end of per function variables
+
+;; These flags enumerate the possible ways a statement/function can
+;; terminate. These flags are used by endCheck() and by the Parser to
+;; detect inconsistent return usage.
+;;
+;; END_UNREACHED is reserved for code paths that are assumed to always be
+;; able to execute (example: throw, continue)
+;;
+;; END_DROPS_OFF indicates if the statement can transfer control to the
+;; next one. Statement such as return dont. A compound statement may have
+;; some branch that drops off control to the next statement.
+;;
+;; END_RETURNS indicates that the statement can return (without arguments)
+;; END_RETURNS_VALUE indicates that the statement can return a value.
+;;
+;; A compound statement such as
+;; if (condition) {
+;;   return value;
+;; }
+;; Will be detected as (END_DROPS_OFF | END_RETURN_VALUE) by endCheck()
+
+(defconst js2-end-unreached     #x0)
+(defconst js2-end-drops-off     #x1)
+(defconst js2-end-returns       #x2)
+(defconst js2-end-returns-value #x4)
+
+;; Rhino awkwardly passes a statementLabel parameter to the
+;; statementHelper() function, the main statement parser, which
+;; is then used by quite a few of the sub-parsers.  We just make
+;; it a buffer-local variable and make sure it's cleaned up properly.
+(js2-deflocal js2-labeled-stmt nil)  ; type `js2-labeled-stmt-node'
+
+;; Similarly, Rhino passes an inForInit boolean through about half
+;; the expression parsers.  We use a dynamically-scoped variable,
+;; which makes it easier to funcall the parsers individually without
+;; worrying about whether they take the parameter or not.
+(js2-deflocal js2-in-for-init nil)
+(js2-deflocal js2-temp-name-counter 0)
+(js2-deflocal js2-parse-stmt-count 0)
+
+(defsubst js2-get-next-temp-name ()
+  (format "$%d" (cl-incf js2-temp-name-counter)))
+
+(defvar js2-parse-interruptable-p t
+  "Set this to nil to force parse to continue until finished.
+This will mostly be useful for interpreters.")
+
+(defvar js2-statements-per-pause 50
+  "Pause after this many statements to check for user input.
+If user input is pending, stop the parse and discard the tree.
+This makes for a smoother user experience for large files.
+You may have to wait a second or two before the highlighting
+and error-reporting appear, but you can always type ahead if
+you wish.  This appears to be more or less how Eclipse, IntelliJ
+and other editors work.")
+
+(js2-deflocal js2-record-comments t
+  "Instructs the scanner to record comments in `js2-scanned-comments'.")
+
+(js2-deflocal js2-scanned-comments nil
+  "List of all comments from the current parse.")
+
+(defcustom js2-mode-indent-inhibit-undo nil
+  "Non-nil to disable collection of Undo information when indenting lines.
+Some users have requested this behavior.  It's nil by default because
+other Emacs modes don't work this way."
+  :type 'boolean
+  :group 'js2-mode)
+
+(defcustom js2-mode-indent-ignore-first-tab nil
+  "If non-nil, ignore first TAB keypress if we look indented properly.
+It's fairly common for users to navigate to an already-indented line
+and press TAB for reassurance that it's been indented.  For this class
+of users, we want the first TAB press on a line to be ignored if the
+line is already indented to one of the precomputed alternatives.
+
+This behavior is only partly implemented.  If you TAB-indent a line,
+navigate to another line, and then navigate back, it fails to clear
+the last-indented variable, so it thinks you've already hit TAB once,
+and performs the indent.  A full solution would involve getting on the
+point-motion hooks for the entire buffer.  If we come across another
+use cases that requires watching point motion, I'll consider doing it.
+
+If you set this variable to nil, then the TAB key will always change
+the indentation of the current line, if more than one alternative
+indentation spot exists."
+  :type 'boolean
+  :group 'js2-mode)
+
+(defvar js2-indent-hook nil
+  "A hook for user-defined indentation rules.
+
+Functions on this hook should expect two arguments:    (LIST INDEX)
+The LIST argument is the list of computed indentation points for
+the current line.  INDEX is the list index of the indentation point
+that `js2-bounce-indent' plans to use.  If INDEX is nil, then the
+indent function is not going to change the current line indentation.
+
+If a hook function on this list returns a non-nil value, then
+`js2-bounce-indent' assumes the hook function has performed its own
+indentation, and will do nothing.  If all hook functions on the list
+return nil, then `js2-bounce-indent' will use its computed indentation
+and reindent the line.
+
+When hook functions on this hook list are called, the variable
+`js2-mode-ast' may or may not be set, depending on whether the
+parse tree is available.  If the variable is nil, you can pass a
+callback to `js2-mode-wait-for-parse', and your callback will be
+called after the new parse tree is built.  This can take some time
+in large files.")
+
+(defface js2-warning
+  `((((class color) (background light))
+     (:underline  "orange"))
+    (((class color) (background dark))
+     (:underline "orange"))
+    (t (:underline t)))
+  "Face for JavaScript warnings."
+  :group 'js2-mode)
+
+(defface js2-error
+  `((((class color) (background light))
+     (:foreground "red"))
+    (((class color) (background dark))
+     (:foreground "red"))
+    (t (:foreground "red")))
+  "Face for JavaScript errors."
+  :group 'js2-mode)
+
+(defface js2-jsdoc-tag
+  '((t :foreground "SlateGray"))
+  "Face used to highlight @whatever tags in jsdoc comments."
+  :group 'js2-mode)
+
+(defface js2-jsdoc-type
+  '((t :foreground "SteelBlue"))
+  "Face used to highlight {FooBar} types in jsdoc comments."
+  :group 'js2-mode)
+
+(defface js2-jsdoc-value
+  '((t :foreground "PeachPuff3"))
+  "Face used to highlight tag values in jsdoc comments."
+  :group 'js2-mode)
+
+(defface js2-function-param
+  '((t :foreground "SeaGreen"))
+  "Face used to highlight function parameters in javascript."
+  :group 'js2-mode)
+
+(defface js2-function-call
+  '((t :inherit default))
+  "Face used to highlight function name in calls."
+  :group 'js2-mode)
+
+(defface js2-instance-member
+  '((t :foreground "DarkOrchid"))
+  "Face used to highlight instance variables in javascript.
+Not currently used."
+  :group 'js2-mode)
+
+(defface js2-private-member
+  '((t :foreground "PeachPuff3"))
+  "Face used to highlight calls to private methods in javascript.
+Not currently used."
+  :group 'js2-mode)
+
+(defface js2-private-function-call
+  '((t :foreground "goldenrod"))
+  "Face used to highlight calls to private functions in javascript.
+Not currently used."
+  :group 'js2-mode)
+
+(defface js2-jsdoc-html-tag-name
+  '((((class color) (min-colors 88) (background light))
+     (:foreground "rosybrown"))
+    (((class color) (min-colors 8) (background dark))
+     (:foreground "yellow"))
+    (((class color) (min-colors 8) (background light))
+     (:foreground "magenta")))
+    "Face used to highlight jsdoc html tag names"
+  :group 'js2-mode)
+
+(defface js2-jsdoc-html-tag-delimiter
+  '((((class color) (min-colors 88) (background light))
+     (:foreground "dark khaki"))
+    (((class color) (min-colors 8) (background dark))
+     (:foreground "green"))
+    (((class color) (min-colors 8) (background light))
+     (:foreground "green")))
+  "Face used to highlight brackets in jsdoc html tags."
+  :group 'js2-mode)
+
+(defface js2-external-variable
+  '((t :foreground "orange"))
+  "Face used to highlight undeclared variable identifiers.")
+
+(defcustom js2-init-hook nil
+  "List of functions to be called after `js2-mode' or
+`js2-minor-mode' has initialized all variables, before parsing
+the buffer for the first time."
+  :type 'hook
+  :group 'js2-mode
+  :version "20130608")
+
+(defcustom js2-post-parse-callbacks nil
+  "List of callback functions invoked after parsing finishes.
+Currently, the main use for this function is to add synthetic
+declarations to `js2-recorded-identifiers', which see."
+  :type 'hook
+  :group 'js2-mode)
+
+(defcustom js2-build-imenu-callbacks nil
+  "List of functions called during Imenu index generation.
+It's a good place to add additional entries to it, using
+`js2-record-imenu-entry'."
+  :type 'hook
+  :group 'js2-mode)
+
+(defcustom js2-highlight-external-variables t
+  "Non-nil to highlight undeclared variable identifiers.
+An undeclared variable is any variable not declared with var or let
+in the current scope or any lexically enclosing scope.  If you use
+such a variable, then you are either expecting it to originate from
+another file, or you've got a potential bug."
+  :type 'boolean
+  :group 'js2-mode)
+
+(defcustom js2-include-jslint-globals t
+  "Non-nil to include the identifiers from JSLint global
+declaration (see http://www.jslint.com/lint.html#global) in the
+buffer-local externs list.  See `js2-additional-externs' for more
+information."
+  :type 'boolean
+  :group 'js2-mode)
+
+(defvar js2-mode-map
+  (let ((map (make-sparse-keymap)))
+    (define-key map [mouse-1] #'js2-mode-show-node)
+    (define-key map (kbd "M-j") #'js2-line-break)
+    (define-key map (kbd "C-c C-e") #'js2-mode-hide-element)
+    (define-key map (kbd "C-c C-s") #'js2-mode-show-element)
+    (define-key map (kbd "C-c C-a") #'js2-mode-show-all)
+    (define-key map (kbd "C-c C-f") #'js2-mode-toggle-hide-functions)
+    (define-key map (kbd "C-c C-t") #'js2-mode-toggle-hide-comments)
+    (define-key map (kbd "C-c C-o") #'js2-mode-toggle-element)
+    (define-key map (kbd "C-c C-w") #'js2-mode-toggle-warnings-and-errors)
+    (define-key map [down-mouse-3] #'js2-down-mouse-3)
+    (when js2-bounce-indent-p
+      (define-key map (kbd "<backtab>") #'js2-indent-bounce-backwards))
+
+    (define-key map [menu-bar javascript]
+      (cons "JavaScript" (make-sparse-keymap "JavaScript")))
+
+    (define-key map [menu-bar javascript customize-js2-mode]
+      '(menu-item "Customize js2-mode" js2-mode-customize
+                  :help "Customize the behavior of this mode"))
+
+    (define-key map [menu-bar javascript js2-force-refresh]
+      '(menu-item "Force buffer refresh" js2-mode-reset
+                  :help "Re-parse the buffer from scratch"))
+
+    (define-key map [menu-bar javascript separator-2]
+      '("--"))
+
+    (define-key map [menu-bar javascript next-error]
+      '(menu-item "Next warning or error" next-error
+                  :enabled (and js2-mode-ast
+                                (or (js2-ast-root-errors js2-mode-ast)
+                                    (js2-ast-root-warnings js2-mode-ast)))
+                  :help "Move to next warning or error"))
+
+    (define-key map [menu-bar javascript display-errors]
+      '(menu-item "Show errors and warnings" js2-mode-display-warnings-and-errors
+                  :visible (not js2-mode-show-parse-errors)
+                  :help "Turn on display of warnings and errors"))
+
+    (define-key map [menu-bar javascript hide-errors]
+      '(menu-item "Hide errors and warnings" js2-mode-hide-warnings-and-errors
+                  :visible js2-mode-show-parse-errors
+                  :help "Turn off display of warnings and errors"))
+
+    (define-key map [menu-bar javascript separator-1]
+      '("--"))
+
+    (define-key map [menu-bar javascript js2-toggle-function]
+      '(menu-item "Show/collapse element" js2-mode-toggle-element
+                  :help "Hide or show function body or comment"))
+
+    (define-key map [menu-bar javascript show-comments]
+      '(menu-item "Show block comments" js2-mode-toggle-hide-comments
+                  :visible js2-mode-comments-hidden
+                  :help "Expand all hidden block comments"))
+
+    (define-key map [menu-bar javascript hide-comments]
+      '(menu-item "Hide block comments" js2-mode-toggle-hide-comments
+                  :visible (not js2-mode-comments-hidden)
+                  :help "Show block comments as /*...*/"))
+
+    (define-key map [menu-bar javascript show-all-functions]
+      '(menu-item "Show function bodies" js2-mode-toggle-hide-functions
+                  :visible js2-mode-functions-hidden
+                  :help "Expand all hidden function bodies"))
+
+    (define-key map [menu-bar javascript hide-all-functions]
+      '(menu-item "Hide function bodies" js2-mode-toggle-hide-functions
+                  :visible (not js2-mode-functions-hidden)
+                  :help "Show {...} for all top-level function bodies"))
+
+    map)
+  "Keymap used in `js2-mode' buffers.")
+
+(defconst js2-mode-identifier-re "[[:alpha:]_$][[:alnum:]_$]*")
+
+(defvar js2-mode-//-comment-re "^\\(\\s-*\\)//.+"
+  "Matches a //-comment line.  Must be first non-whitespace on line.
+First match-group is the leading whitespace.")
+
+(defvar js2-mode-hook nil)
+
+(js2-deflocal js2-mode-ast nil "Private variable.")
+(js2-deflocal js2-mode-parse-timer nil "Private variable.")
+(js2-deflocal js2-mode-buffer-dirty-p nil "Private variable.")
+(js2-deflocal js2-mode-parsing nil "Private variable.")
+(js2-deflocal js2-mode-node-overlay nil)
+
+(defvar js2-mode-show-overlay js2-mode-dev-mode-p
+  "Debug:  Non-nil to highlight AST nodes on mouse-down.")
+
+(js2-deflocal js2-mode-fontifications nil "Private variable")
+(js2-deflocal js2-mode-deferred-properties nil "Private variable")
+(js2-deflocal js2-imenu-recorder nil "Private variable")
+(js2-deflocal js2-imenu-function-map nil "Private variable")
+
+(defvar js2-paragraph-start
+  "\\(@[[:alpha:]]+\\>\\|$\\)")
+
+;; Note that we also set a 'c-in-sws text property in html comments,
+;; so that `c-forward-sws' and `c-backward-sws' work properly.
+(defvar js2-syntactic-ws-start
+  "\\s \\|/[*/]\\|[\n\r]\\|\\\\[\n\r]\\|\\s!\\|<!--\\|^\\s-*-->")
+
+(defvar js2-syntactic-ws-end
+  "\\s \\|[\n\r/]\\|\\s!")
+
+(defvar js2-syntactic-eol
+  (concat "\\s *\\(/\\*[^*\n\r]*"
+          "\\(\\*+[^*\n\r/][^*\n\r]*\\)*"
+          "\\*+/\\s *\\)*"
+          "\\(//\\|/\\*[^*\n\r]*"
+          "\\(\\*+[^*\n\r/][^*\n\r]*\\)*$"
+          "\\|\\\\$\\|$\\)")
+  "Copied from `java-mode'.  Needed for some cc-engine functions.")
+
+(defvar js2-comment-prefix-regexp
+  "//+\\|\\**")
+
+(defvar js2-comment-start-skip
+  "\\(//+\\|/\\*+\\)\\s *")
+
+(defvar js2-mode-verbose-parse-p js2-mode-dev-mode-p
+  "Non-nil to emit status messages during parsing.")
+
+(defvar js2-mode-functions-hidden nil "Private variable.")
+(defvar js2-mode-comments-hidden nil "Private variable.")
+
+(defvar js2-mode-syntax-table
+  (let ((table (make-syntax-table)))
+    (c-populate-syntax-table table)
+    table)
+  "Syntax table used in `js2-mode' buffers.")
+
+(defvar js2-mode-abbrev-table nil
+  "Abbrev table in use in `js2-mode' buffers.")
+(define-abbrev-table 'js2-mode-abbrev-table ())
+
+(defvar js2-mode-pending-parse-callbacks nil
+  "List of functions waiting to be notified that parse is finished.")
+
+(defvar js2-mode-last-indented-line -1)
+
+;;; Localizable error and warning messages
+
+;; Messages are copied from Rhino's Messages.properties.
+;; Many of the Java-specific messages have been elided.
+;; Add any js2-specific ones at the end, so we can keep
+;; this file synced with changes to Rhino's.
+
+(defvar js2-message-table
+  (make-hash-table :test 'equal :size 250)
+  "Contains localized messages for `js2-mode'.")
+
+;; TODO(stevey):  construct this table at compile-time.
+(defmacro js2-msg (key &rest strings)
+  `(puthash ,key (concat ,@strings)
+            js2-message-table))
+
+(defun js2-get-msg (msg-key)
+  "Look up a localized message.
+MSG-KEY is a list of (MSG ARGS).  If the message takes parameters,
+the correct number of ARGS must be provided."
+  (let* ((key (if (listp msg-key) (car msg-key) msg-key))
+         (args (if (listp msg-key) (cdr msg-key)))
+         (msg (gethash key js2-message-table)))
+    (if msg
+        (apply #'format msg args)
+      key)))  ; default to showing the key
+
+(js2-msg "msg.dup.parms"
+         "Duplicate parameter name '%s'.")
+
+(js2-msg "msg.too.big.jump"
+         "Program too complex: jump offset too big.")
+
+(js2-msg "msg.too.big.index"
+         "Program too complex: internal index exceeds 64K limit.")
+
+(js2-msg "msg.while.compiling.fn"
+         "Encountered code generation error while compiling function '%s': %s")
+
+(js2-msg "msg.while.compiling.script"
+         "Encountered code generation error while compiling script: %s")
+
+;; Context
+(js2-msg "msg.ctor.not.found"
+         "Constructor for '%s' not found.")
+
+(js2-msg "msg.not.ctor"
+         "'%s' is not a constructor.")
+
+;; FunctionObject
+(js2-msg "msg.varargs.ctor"
+         "Method or constructor '%s' must be static "
+         "with the signature (Context cx, Object[] args, "
+         "Function ctorObj, boolean inNewExpr) "
+         "to define a variable arguments constructor.")
+
+(js2-msg "msg.varargs.fun"
+         "Method '%s' must be static with the signature "
+         "(Context cx, Scriptable thisObj, Object[] args, Function funObj) "
+         "to define a variable arguments function.")
+
+(js2-msg "msg.incompat.call"
+         "Method '%s' called on incompatible object.")
+
+(js2-msg "msg.bad.parms"
+         "Unsupported parameter type '%s' in method '%s'.")
+
+(js2-msg "msg.bad.method.return"
+         "Unsupported return type '%s' in method '%s'.")
+
+(js2-msg "msg.bad.ctor.return"
+         "Construction of objects of type '%s' is not supported.")
+
+(js2-msg "msg.no.overload"
+         "Method '%s' occurs multiple times in class '%s'.")
+
+(js2-msg "msg.method.not.found"
+         "Method '%s' not found in '%s'.")
+
+;; IRFactory
+
+(js2-msg "msg.bad.for.in.lhs"
+         "Invalid left-hand side of for..in loop.")
+
+(js2-msg "msg.mult.index"
+         "Only one variable allowed in for..in loop.")
+
+(js2-msg "msg.bad.for.in.destruct"
+         "Left hand side of for..in loop must be an array of "
+         "length 2 to accept key/value pair.")
+
+(js2-msg "msg.cant.convert"
+         "Can't convert to type '%s'.")
+
+(js2-msg "msg.bad.assign.left"
+         "Invalid assignment left-hand side.")
+
+(js2-msg "msg.bad.decr"
+         "Invalid decerement operand.")
+
+(js2-msg "msg.bad.incr"
+         "Invalid increment operand.")
+
+(js2-msg "msg.bad.yield"
+         "yield must be in a function.")
+
+(js2-msg "msg.yield.parenthesized"
+         "yield expression must be parenthesized.")
+
+;; NativeGlobal
+(js2-msg "msg.cant.call.indirect"
+          "Function '%s' must be called directly, and not by way of a "
+          "function of another name.")
+
+(js2-msg "msg.eval.nonstring"
+          "Calling eval() with anything other than a primitive "
+          "string value will simply return the value. "
+          "Is this what you intended?")
+
+(js2-msg "msg.eval.nonstring.strict"
+         "Calling eval() with anything other than a primitive "
+         "string value is not allowed in strict mode.")
+
+(js2-msg "msg.bad.destruct.op"
+         "Invalid destructuring assignment operator")
+
+;; NativeCall
+(js2-msg "msg.only.from.new"
+         "'%s' may only be invoked from a `new' expression.")
+
+(js2-msg "msg.deprec.ctor"
+         "The '%s' constructor is deprecated.")
+
+;; NativeFunction
+(js2-msg "msg.no.function.ref.found"
+         "no source found to decompile function reference %s")
+
+(js2-msg "msg.arg.isnt.array"
+         "second argument to Function.prototype.apply must be an array")
+
+;; NativeGlobal
+(js2-msg "msg.bad.esc.mask"
+         "invalid string escape mask")
+
+;; NativeRegExp
+(js2-msg "msg.bad.quant"
+  "Invalid quantifier %s")
+
+(js2-msg "msg.overlarge.backref"
+  "Overly large back reference %s")
+
+(js2-msg "msg.overlarge.min"
+  "Overly large minimum %s")
+
+(js2-msg "msg.overlarge.max"
+  "Overly large maximum %s")
+
+(js2-msg "msg.zero.quant"
+  "Zero quantifier %s")
+
+(js2-msg "msg.max.lt.min"
+  "Maximum %s less than minimum")
+
+(js2-msg "msg.unterm.quant"
+  "Unterminated quantifier %s")
+
+(js2-msg "msg.unterm.paren"
+  "Unterminated parenthetical %s")
+
+(js2-msg "msg.unterm.class"
+  "Unterminated character class %s")
+
+(js2-msg "msg.bad.range"
+  "Invalid range in character class.")
+
+(js2-msg "msg.trail.backslash"
+  "Trailing \\ in regular expression.")
+
+(js2-msg "msg.re.unmatched.right.paren"
+  "unmatched ) in regular expression.")
+
+(js2-msg "msg.no.regexp"
+  "Regular expressions are not available.")
+
+(js2-msg "msg.bad.backref"
+  "back-reference exceeds number of capturing parentheses.")
+
+(js2-msg "msg.bad.regexp.compile"
+         "Only one argument may be specified if the first "
+         "argument to RegExp.prototype.compile is a RegExp object.")
+
+;; Parser
+(js2-msg "msg.got.syntax.errors"
+         "Compilation produced %s syntax errors.")
+
+(js2-msg "msg.var.redecl"
+         "TypeError: redeclaration of var %s.")
+
+(js2-msg "msg.const.redecl"
+         "TypeError: redeclaration of const %s.")
+
+(js2-msg "msg.let.redecl"
+         "TypeError: redeclaration of variable %s.")
+
+(js2-msg "msg.parm.redecl"
+         "TypeError: redeclaration of formal parameter %s.")
+
+(js2-msg "msg.fn.redecl"
+         "TypeError: redeclaration of function %s.")
+
+(js2-msg "msg.let.decl.not.in.block"
+         "SyntaxError: let declaration not directly within block")
+
+(js2-msg "msg.mod.import.decl.at.top.level"
+         "SyntaxError: import declarations may only appear at the top level")
+
+(js2-msg "msg.mod.as.after.reserved.word"
+         "SyntaxError: missing keyword 'as' after reserved word %s")
+
+(js2-msg "msg.mod.rc.after.import.spec.list"
+         "SyntaxError: missing '}' after module specifier list")
+
+(js2-msg "msg.mod.from.after.import.spec.set"
+         "SyntaxError: missing keyword 'from' after import specifier set")
+
+(js2-msg "msg.mod.declaration.after.import"
+         "SyntaxError: missing declaration after 'import' keyword")
+
+(js2-msg "msg.mod.spec.after.from"
+         "SyntaxError: missing module specifier after 'from' keyword")
+
+(js2-msg "msg.mod.export.decl.at.top.level"
+         "SyntaxError: export declarations may only appear at top level")
+
+(js2-msg "msg.mod.rc.after.export.spec.list"
+         "SyntaxError: missing '}' after export specifier list")
+
+;; NodeTransformer
+(js2-msg "msg.dup.label"
+         "duplicated label")
+
+(js2-msg "msg.undef.label"
+         "undefined label")
+
+(js2-msg "msg.bad.break"
+         "unlabelled break must be inside loop or switch")
+
+(js2-msg "msg.continue.outside"
+         "continue must be inside loop")
+
+(js2-msg "msg.continue.nonloop"
+         "continue can only use labels of iteration statements")
+
+(js2-msg "msg.bad.throw.eol"
+         "Line terminator is not allowed between the throw "
+         "keyword and throw expression.")
+
+(js2-msg "msg.unnamed.function.stmt" ; added by js2-mode
+         "function statement requires a name")
+
+(js2-msg "msg.no.paren.parms"
+         "missing ( before function parameters.")
+
+(js2-msg "msg.no.parm"
+         "missing formal parameter")
+
+(js2-msg "msg.no.paren.after.parms"
+         "missing ) after formal parameters")
+
+(js2-msg "msg.no.default.after.default.param" ; added by js2-mode
+         "parameter without default follows parameter with default")
+
+(js2-msg "msg.param.after.rest" ; added by js2-mode
+         "parameter after rest parameter")
+
+(js2-msg "msg.bad.arrow.args" ; added by js2-mode
+         "invalid arrow-function arguments (parentheses around the arrow-function may help)")
+
+(js2-msg "msg.no.brace.body"
+         "missing '{' before function body")
+
+(js2-msg "msg.no.brace.after.body"
+         "missing } after function body")
+
+(js2-msg "msg.no.paren.cond"
+         "missing ( before condition")
+
+(js2-msg "msg.no.paren.after.cond"
+         "missing ) after condition")
+
+(js2-msg "msg.no.semi.stmt"
+         "missing ; before statement")
+
+(js2-msg "msg.missing.semi"
+         "missing ; after statement")
+
+(js2-msg "msg.no.name.after.dot"
+         "missing name after . operator")
+
+(js2-msg "msg.no.name.after.coloncolon"
+         "missing name after :: operator")
+
+(js2-msg "msg.no.name.after.dotdot"
+         "missing name after .. operator")
+
+(js2-msg "msg.no.name.after.xmlAttr"
+         "missing name after .@")
+
+(js2-msg "msg.no.bracket.index"
+         "missing ] in index expression")
+
+(js2-msg "msg.no.paren.switch"
+         "missing ( before switch expression")
+
+(js2-msg "msg.no.paren.after.switch"
+         "missing ) after switch expression")
+
+(js2-msg "msg.no.brace.switch"
+         "missing '{' before switch body")
+
+(js2-msg "msg.bad.switch"
+         "invalid switch statement")
+
+(js2-msg "msg.no.colon.case"
+         "missing : after case expression")
+
+(js2-msg "msg.double.switch.default"
+         "double default label in the switch statement")
+
+(js2-msg "msg.no.while.do"
+         "missing while after do-loop body")
+
+(js2-msg "msg.no.paren.for"
+         "missing ( after for")
+
+(js2-msg "msg.no.semi.for"
+         "missing ; after for-loop initializer")
+
+(js2-msg "msg.no.semi.for.cond"
+         "missing ; after for-loop condition")
+
+(js2-msg "msg.in.after.for.name"
+         "missing in or of after for")
+
+(js2-msg "msg.no.paren.for.ctrl"
+         "missing ) after for-loop control")
+
+(js2-msg "msg.no.paren.with"
+         "missing ( before with-statement object")
+
+(js2-msg "msg.no.paren.after.with"
+         "missing ) after with-statement object")
+
+(js2-msg "msg.no.paren.after.let"
+         "missing ( after let")
+
+(js2-msg "msg.no.paren.let"
+         "missing ) after variable list")
+
+(js2-msg "msg.no.curly.let"
+         "missing } after let statement")
+
+(js2-msg "msg.bad.return"
+         "invalid return")
+
+(js2-msg "msg.no.brace.block"
+         "missing } in compound statement")
+
+(js2-msg "msg.bad.label"
+         "invalid label")
+
+(js2-msg "msg.bad.var"
+         "missing variable name")
+
+(js2-msg "msg.bad.var.init"
+         "invalid variable initialization")
+
+(js2-msg "msg.no.colon.cond"
+         "missing : in conditional expression")
+
+(js2-msg "msg.no.paren.arg"
+         "missing ) after argument list")
+
+(js2-msg "msg.no.bracket.arg"
+         "missing ] after element list")
+
+(js2-msg "msg.bad.prop"
+         "invalid property id")
+
+(js2-msg "msg.no.colon.prop"
+         "missing : after property id")
+
+(js2-msg "msg.no.brace.prop"
+         "missing } after property list")
+
+(js2-msg "msg.no.paren"
+         "missing ) in parenthetical")
+
+(js2-msg "msg.reserved.id"
+         "'%s' is a reserved identifier")
+
+(js2-msg "msg.no.paren.catch"
+         "missing ( before catch-block condition")
+
+(js2-msg "msg.bad.catchcond"
+         "invalid catch block condition")
+
+(js2-msg "msg.catch.unreachable"
+         "any catch clauses following an unqualified catch are unreachable")
+
+(js2-msg "msg.no.brace.try"
+         "missing '{' before try block")
+
+(js2-msg "msg.no.brace.catchblock"
+         "missing '{' before catch-block body")
+
+(js2-msg "msg.try.no.catchfinally"
+         "'try' without 'catch' or 'finally'")
+
+(js2-msg "msg.no.return.value"
+         "function %s does not always return a value")
+
+(js2-msg "msg.anon.no.return.value"
+         "anonymous function does not always return a value")
+
+(js2-msg "msg.return.inconsistent"
+         "return statement is inconsistent with previous usage")
+
+(js2-msg "msg.generator.returns"
+         "TypeError: legacy generator function '%s' returns a value")
+
+(js2-msg "msg.anon.generator.returns"
+         "TypeError: anonymous legacy generator function returns a value")
+
+(js2-msg "msg.syntax"
+         "syntax error")
+
+(js2-msg "msg.unexpected.eof"
+         "Unexpected end of file")
+
+(js2-msg "msg.XML.bad.form"
+         "illegally formed XML syntax")
+
+(js2-msg "msg.XML.not.available"
+         "XML runtime not available")
+
+(js2-msg "msg.too.deep.parser.recursion"
+         "Too deep recursion while parsing")
+
+(js2-msg "msg.no.side.effects"
+         "Code has no side effects")
+
+(js2-msg "msg.extra.trailing.comma"
+         "Trailing comma is not supported in some browsers")
+
+(js2-msg "msg.array.trailing.comma"
+         "Trailing comma yields different behavior across browsers")
+
+(js2-msg "msg.equal.as.assign"
+         (concat "Test for equality (==) mistyped as assignment (=)?"
+                 " (parenthesize to suppress warning)"))
+
+(js2-msg "msg.var.hides.arg"
+         "Variable %s hides argument")
+
+(js2-msg "msg.destruct.assign.no.init"
+         "Missing = in destructuring declaration")
+
+;; ScriptRuntime
+(js2-msg "msg.no.properties"
+         "%s has no properties.")
+
+(js2-msg "msg.invalid.iterator"
+         "Invalid iterator value")
+
+(js2-msg "msg.iterator.primitive"
+         "__iterator__ returned a primitive value")
+
+(js2-msg "msg.assn.create.strict"
+         "Assignment to undeclared variable %s")
+
+(js2-msg "msg.undeclared.variable"  ; added by js2-mode
+         "Undeclared variable or function '%s'")
+
+(js2-msg "msg.ref.undefined.prop"
+         "Reference to undefined property '%s'")
+
+(js2-msg "msg.prop.not.found"
+         "Property %s not found.")
+
+(js2-msg "msg.invalid.type"
+         "Invalid JavaScript value of type %s")
+
+(js2-msg "msg.primitive.expected"
+         "Primitive type expected (had %s instead)")
+
+(js2-msg "msg.namespace.expected"
+         "Namespace object expected to left of :: (found %s instead)")
+
+(js2-msg "msg.null.to.object"
+         "Cannot convert null to an object.")
+
+(js2-msg "msg.undef.to.object"
+         "Cannot convert undefined to an object.")
+
+(js2-msg "msg.cyclic.value"
+         "Cyclic %s value not allowed.")
+
+(js2-msg "msg.is.not.defined"
+         "'%s' is not defined.")
+
+(js2-msg "msg.undef.prop.read"
+         "Cannot read property '%s' from %s")
+
+(js2-msg "msg.undef.prop.write"
+         "Cannot set property '%s' of %s to '%s'")
+
+(js2-msg "msg.undef.prop.delete"
+         "Cannot delete property '%s' of %s")
+
+(js2-msg "msg.undef.method.call"
+         "Cannot call method '%s' of %s")
+
+(js2-msg "msg.undef.with"
+         "Cannot apply 'with' to %s")
+
+(js2-msg "msg.isnt.function"
+         "%s is not a function, it is %s.")
+
+(js2-msg "msg.isnt.function.in"
+         "Cannot call property %s in object %s. "
+         "It is not a function, it is '%s'.")
+
+(js2-msg "msg.function.not.found"
+         "Cannot find function %s.")
+
+(js2-msg "msg.function.not.found.in"
+         "Cannot find function %s in object %s.")
+
+(js2-msg "msg.isnt.xml.object"
+         "%s is not an xml object.")
+
+(js2-msg "msg.no.ref.to.get"
+         "%s is not a reference to read reference value.")
+
+(js2-msg "msg.no.ref.to.set"
+         "%s is not a reference to set reference value to %s.")
+
+(js2-msg "msg.no.ref.from.function"
+         "Function %s can not be used as the left-hand "
+         "side of assignment or as an operand of ++ or -- operator.")
+
+(js2-msg "msg.bad.default.value"
+         "Object's getDefaultValue() method returned an object.")
+
+(js2-msg "msg.instanceof.not.object"
+         "Can't use instanceof on a non-object.")
+
+(js2-msg "msg.instanceof.bad.prototype"
+         "'prototype' property of %s is not an object.")
+
+(js2-msg "msg.bad.radix"
+         "illegal radix %s.")
+
+;; ScriptableObject
+(js2-msg "msg.default.value"
+         "Cannot find default value for object.")
+
+(js2-msg "msg.zero.arg.ctor"
+         "Cannot load class '%s' which has no zero-parameter constructor.")
+
+(js2-msg "msg.ctor.multiple.parms"
+         "Can't define constructor or class %s since more than "
+         "one constructor has multiple parameters.")
+
+(js2-msg "msg.extend.scriptable"
+         "%s must extend ScriptableObject in order to define property %s.")
+
+(js2-msg "msg.bad.getter.parms"
+         "In order to define a property, getter %s must have zero "
+         "parameters or a single ScriptableObject parameter.")
+
+(js2-msg "msg.obj.getter.parms"
+         "Expected static or delegated getter %s to take "
+         "a ScriptableObject parameter.")
+
+(js2-msg "msg.getter.static"
+         "Getter and setter must both be static or neither be static.")
+
+(js2-msg "msg.setter.return"
+         "Setter must have void return type: %s")
+
+(js2-msg "msg.setter2.parms"
+         "Two-parameter setter must take a ScriptableObject as "
+         "its first parameter.")
+
+(js2-msg "msg.setter1.parms"
+         "Expected single parameter setter for %s")
+
+(js2-msg "msg.setter2.expected"
+         "Expected static or delegated setter %s to take two parameters.")
+
+(js2-msg "msg.setter.parms"
+         "Expected either one or two parameters for setter.")
+
+(js2-msg "msg.setter.bad.type"
+         "Unsupported parameter type '%s' in setter '%s'.")
+
+(js2-msg "msg.add.sealed"
+         "Cannot add a property to a sealed object: %s.")
+
+(js2-msg "msg.remove.sealed"
+         "Cannot remove a property from a sealed object: %s.")
+
+(js2-msg "msg.modify.sealed"
+         "Cannot modify a property of a sealed object: %s.")
+
+(js2-msg "msg.modify.readonly"
+         "Cannot modify readonly property: %s.")
+
+;; TokenStream
+(js2-msg "msg.missing.exponent"
+         "missing exponent")
+
+(js2-msg "msg.caught.nfe"
+         "number format error")
+
+(js2-msg "msg.unterminated.string.lit"
+         "unterminated string literal")
+
+(js2-msg "msg.unterminated.comment"
+         "unterminated comment")
+
+(js2-msg "msg.unterminated.re.lit"
+         "unterminated regular expression literal")
+
+(js2-msg "msg.invalid.re.flag"
+         "invalid flag after regular expression")
+
+(js2-msg "msg.no.re.input.for"
+         "no input for %s")
+
+(js2-msg "msg.illegal.character"
+         "illegal character")
+
+(js2-msg "msg.invalid.escape"
+         "invalid Unicode escape sequence")
+
+(js2-msg "msg.bad.namespace"
+         "not a valid default namespace statement. "
+         "Syntax is: default xml namespace = EXPRESSION;")
+
+;; TokensStream warnings
+(js2-msg "msg.bad.octal.literal"
+         "illegal octal literal digit %s; "
+         "interpreting it as a decimal digit")
+
+(js2-msg "msg.missing.hex.digits"
+         "missing hexadecimal digits after '0x'")
+
+(js2-msg "msg.missing.binary.digits"
+         "missing binary digits after '0b'")
+
+(js2-msg "msg.missing.octal.digits"
+         "missing octal digits after '0o'")
+
+(js2-msg "msg.script.is.not.constructor"
+         "Script objects are not constructors.")
+
+;; Arrays
+(js2-msg "msg.arraylength.bad"
+         "Inappropriate array length.")
+
+;; Arrays
+(js2-msg "msg.arraylength.too.big"
+         "Array length %s exceeds supported capacity limit.")
+
+;; URI
+(js2-msg "msg.bad.uri"
+         "Malformed URI sequence.")
+
+;; Number
+(js2-msg "msg.bad.precision"
+         "Precision %s out of range.")
+
+;; NativeGenerator
+(js2-msg "msg.send.newborn"
+         "Attempt to send value to newborn generator")
+
+(js2-msg "msg.already.exec.gen"
+         "Already executing generator")
+
+(js2-msg "msg.StopIteration.invalid"
+         "StopIteration may not be changed to an arbitrary object.")
+
+;; Interpreter
+(js2-msg "msg.yield.closing"
+         "Yield from closing generator")
+
+;; Classes
+(js2-msg "msg.unnamed.class.stmt" ; added by js2-mode
+         "class statement requires a name")
+
+(js2-msg "msg.class.unexpected.comma" ; added by js2-mode
+         "unexpected ',' between class properties")
+
+(js2-msg "msg.unexpected.static" ; added by js2-mode
+         "unexpected 'static'")
+
+(js2-msg "msg.missing.extends" ; added by js2-mode
+         "name is required after extends")
+
+(js2-msg "msg.no.brace.class" ; added by js2-mode
+         "missing '{' before class body")
+
+(js2-msg "msg.missing.computed.rb" ; added by js2-mode
+         "missing ']' after computed property expression")
+
+;;; Tokens Buffer
+
+(defconst js2-ti-max-lookahead 2)
+(defconst js2-ti-ntokens (1+ js2-ti-max-lookahead))
+
+(defun js2-new-token (offset)
+  (let ((token (make-js2-token (+ offset js2-ts-cursor))))
+    (setq js2-ti-tokens-cursor (mod (1+ js2-ti-tokens-cursor) js2-ti-ntokens))
+    (aset js2-ti-tokens js2-ti-tokens-cursor token)
+    token))
+
+(defsubst js2-current-token ()
+  (aref js2-ti-tokens js2-ti-tokens-cursor))
+
+(defsubst js2-current-token-string ()
+  (js2-token-string (js2-current-token)))
+
+(defsubst js2-current-token-type ()
+  (js2-token-type (js2-current-token)))
+
+(defsubst js2-current-token-beg ()
+  (js2-token-beg (js2-current-token)))
+
+(defsubst js2-current-token-end ()
+  (js2-token-end (js2-current-token)))
+
+(defun js2-current-token-len ()
+  (let ((token (js2-current-token)))
+    (- (js2-token-end token)
+       (js2-token-beg token))))
+
+(defun js2-ts-seek (state)
+  (setq js2-ts-lineno (js2-ts-state-lineno state)
+        js2-ts-cursor (js2-ts-state-cursor state)
+        js2-ti-tokens (js2-ts-state-tokens state)
+        js2-ti-tokens-cursor (js2-ts-state-tokens-cursor state)
+        js2-ti-lookahead (js2-ts-state-lookahead state)))
+
+;;; Utilities
+
+(defun js2-delete-if (predicate list)
+  "Remove all items satisfying PREDICATE in LIST."
+  (cl-loop for item in list
+           if (not (funcall predicate item))
+           collect item))
+
+(defun js2-position (element list)
+  "Find 0-indexed position of ELEMENT in LIST comparing with `eq'.
+Returns nil if element is not found in the list."
+  (let ((count 0)
+        found)
+    (while (and list (not found))
+      (if (eq element (car list))
+          (setq found t)
+        (setq count (1+ count)
+              list (cdr list))))
+    (if found count)))
+
+(defun js2-find-if (predicate list)
+  "Find first item satisfying PREDICATE in LIST."
+  (let (result)
+    (while (and list (not result))
+      (if (funcall predicate (car list))
+          (setq result (car list)))
+      (setq list (cdr list)))
+    result))
+
+(defmacro js2-time (form)
+  "Evaluate FORM, discard result, and return elapsed time in sec."
+  (declare (debug t))
+  (let ((beg (make-symbol "--js2-time-beg--"))
+        (delta (make-symbol "--js2-time-end--")))
+    `(let ((,beg (current-time))
+           ,delta)
+       ,form
+       (/ (truncate (* (- (float-time (current-time))
+                          (float-time ,beg))
+                       10000))
+          10000.0))))
+
+(defsubst js2-same-line (pos)
+  "Return t if POS is on the same line as current point."
+  (and (>= pos (point-at-bol))
+       (<= pos (point-at-eol))))
+
+(defun js2-code-bug ()
+  "Signal an error when we encounter an unexpected code path."
+  (error "failed assertion"))
+
+(defsubst js2-record-text-property (beg end prop value)
+  "Record a text property to set when parsing finishes."
+  (push (list beg end prop value) js2-mode-deferred-properties))
+
+;; I'd like to associate errors with nodes, but for now the
+;; easiest thing to do is get the context info from the last token.
+(defun js2-record-parse-error (msg &optional arg pos len)
+  (push (list (list msg arg)
+              (or pos (js2-current-token-beg))
+              (or len (js2-current-token-len)))
+        js2-parsed-errors))
+
+(defun js2-report-error (msg &optional msg-arg pos len)
+  "Signal a syntax error or record a parse error."
+  (if js2-recover-from-parse-errors
+      (js2-record-parse-error msg msg-arg pos len)
+  (signal 'js2-syntax-error
+          (list msg
+                js2-ts-lineno
+                (save-excursion
+                  (goto-char js2-ts-cursor)
+                  (current-column))
+                js2-ts-hit-eof))))
+
+(defun js2-report-warning (msg &optional msg-arg pos len face)
+  (if js2-compiler-report-warning-as-error
+      (js2-report-error msg msg-arg pos len)
+    (push (list (list msg msg-arg)
+                (or pos (js2-current-token-beg))
+                (or len (js2-current-token-len))
+                face)
+          js2-parsed-warnings)))
+
+(defun js2-add-strict-warning (msg-id &optional msg-arg beg end)
+  (if js2-compiler-strict-mode
+      (js2-report-warning msg-id msg-arg beg
+                          (and beg end (- end beg)))))
+
+(put 'js2-syntax-error 'error-conditions
+     '(error syntax-error js2-syntax-error))
+(put 'js2-syntax-error 'error-message "Syntax error")
+
+(put 'js2-parse-error 'error-conditions
+     '(error parse-error js2-parse-error))
+(put 'js2-parse-error 'error-message "Parse error")
+
+(defmacro js2-clear-flag (flags flag)
+  `(setq ,flags (logand ,flags (lognot ,flag))))
+
+(defmacro js2-set-flag (flags flag)
+  "Logical-or FLAG into FLAGS."
+  `(setq ,flags (logior ,flags ,flag)))
+
+(defsubst js2-flag-set-p (flags flag)
+  (/= 0 (logand flags flag)))
+
+(defsubst js2-flag-not-set-p (flags flag)
+  (zerop (logand flags flag)))
+
+(defmacro js2-with-underscore-as-word-syntax (&rest body)
+  "Evaluate BODY with the _ character set to be word-syntax."
+  (declare (indent 0) (debug t))
+  (let ((old-syntax (make-symbol "old-syntax")))
+  `(let ((,old-syntax (string (char-syntax ?_))))
+     (unwind-protect
+         (progn
+           (modify-syntax-entry ?_ "w" js2-mode-syntax-table)
+           ,@body)
+       (modify-syntax-entry ?_ ,old-syntax js2-mode-syntax-table)))))
+
+;;; AST struct and function definitions
+
+;; flags for ast node property 'member-type (used for e4x operators)
+(defvar js2-property-flag    #x1 "Property access: element is valid name.")
+(defvar js2-attribute-flag   #x2 "x.@y or x..@y.")
+(defvar js2-descendants-flag #x4 "x..y or x..@i.")
+
+(defsubst js2-relpos (pos anchor)
+  "Convert POS to be relative to ANCHOR.
+If POS is nil, returns nil."
+  (and pos (- pos anchor)))
+
+(defun js2-make-pad (indent)
+  (if (zerop indent)
+      ""
+    (make-string (* indent js2-basic-offset) ? )))
+
+(defun js2-visit-ast (node callback)
+  "Visit every node in ast NODE with visitor CALLBACK.
+
+CALLBACK is a function that takes two arguments:  (NODE END-P).  It is
+called twice:  once to visit the node, and again after all the node's
+children have been processed.  The END-P argument is nil on the first
+call and non-nil on the second call.  The return value of the callback
+affects the traversal:  if non-nil, the children of NODE are processed.
+If the callback returns nil, or if the node has no children, then the
+callback is called immediately with a non-nil END-P argument.
+
+The node traversal is approximately lexical-order, although there
+are currently no guarantees around this."
+  (when node
+    (let ((vfunc (get (aref node 0) 'js2-visitor)))
+      ;; visit the node
+      (when  (funcall callback node nil)
+        ;; visit the kids
+        (cond
+         ((eq vfunc 'js2-visit-none)
+          nil)                            ; don't even bother calling it
+         ;; Each AST node type has to define a `js2-visitor' function
+         ;; that takes a node and a callback, and calls `js2-visit-ast'
+         ;; on each child of the node.
+         (vfunc
+          (funcall vfunc node callback))
+         (t
+          (error "%s does not define a visitor-traversal function"
+                 (aref node 0)))))
+      ;; call the end-visit
+      (funcall callback node t))))
+
+(cl-defstruct (js2-node
+               (:constructor nil))  ; abstract
+  "Base AST node type."
+  (type -1)  ; token type
+  (pos -1)   ; start position of this AST node in parsed input
+  (len 1)    ; num characters spanned by the node
+  props      ; optional node property list (an alist)
+  parent)    ; link to parent node; null for root
+
+(defsubst js2-node-get-prop (node prop &optional default)
+  (or (cadr (assoc prop (js2-node-props node))) default))
+
+(defsubst js2-node-set-prop (node prop value)
+  (setf (js2-node-props node)
+        (cons (list prop value) (js2-node-props node))))
+
+(defun js2-fixup-starts (n nodes)
+  "Adjust the start positions of NODES to be relative to N.
+Any node in the list may be nil, for convenience."
+  (dolist (node nodes)
+    (when node
+      (setf (js2-node-pos node) (- (js2-node-pos node)
+                                   (js2-node-pos n))))))
+
+(defun js2-node-add-children (parent &rest nodes)
+  "Set parent node of NODES to PARENT, and return PARENT.
+Does nothing if we're not recording parent links.
+If any given node in NODES is nil, doesn't record that link."
+  (js2-fixup-starts parent nodes)
+  (dolist (node nodes)
+    (and node
+         (setf (js2-node-parent node) parent))))
+
+;; Non-recursive since it's called a frightening number of times.
+(defun js2-node-abs-pos (n)
+  (let ((pos (js2-node-pos n)))
+    (while (setq n (js2-node-parent n))
+      (setq pos (+ pos (js2-node-pos n))))
+    pos))
+
+(defsubst js2-node-abs-end (n)
+  "Return absolute buffer position of end of N."
+  (+ (js2-node-abs-pos n) (js2-node-len n)))
+
+;; It's important to make sure block nodes have a Lisp list for the
+;; child nodes, to limit printing recursion depth in an AST that
+;; otherwise consists of defstruct vectors.  Emacs will crash printing
+;; a sufficiently large vector tree.
+
+(cl-defstruct (js2-block-node
+               (:include js2-node)
+               (:constructor nil)
+               (:constructor make-js2-block-node (&key (type js2-BLOCK)
+                                                       (pos (js2-current-token-beg))
+                                                       len
+                                                       props
+                                                       kids)))
+  "A block of statements."
+  kids)  ; a Lisp list of the child statement nodes
+
+(put 'cl-struct-js2-block-node 'js2-visitor 'js2-visit-block)
+(put 'cl-struct-js2-block-node 'js2-printer 'js2-print-block)
+
+(defun js2-visit-block (ast callback)
+  "Visit the `js2-block-node' children of AST."
+  (dolist (kid (js2-block-node-kids ast))
+    (js2-visit-ast kid callback)))
+
+(defun js2-print-block (n i)
+  (let ((pad (js2-make-pad i)))
+    (insert pad "{\n")
+    (dolist (kid (js2-block-node-kids n))
+      (js2-print-ast kid (1+ i)))
+    (insert pad "}")))
+
+(cl-defstruct (js2-scope
+               (:include js2-block-node)
+               (:constructor nil)
+               (:constructor make-js2-scope (&key (type js2-BLOCK)
+                                                  (pos (js2-current-token-beg))
+                                                  len
+                                                  kids)))
+  ;; The symbol-table is a LinkedHashMap<String,Symbol> in Rhino.
+  ;; I don't have one of those handy, so I'll use an alist for now.
+  ;; It's as fast as an emacs hashtable for up to about 50 elements,
+  ;; and is much lighter-weight to construct (both CPU and mem).
+  ;; The keys are interned strings (symbols) for faster lookup.
+  ;; Should switch to hybrid alist/hashtable eventually.
+  symbol-table  ; an alist of (symbol . js2-symbol)
+  parent-scope  ; a `js2-scope'
+  top)          ; top-level `js2-scope' (script/function)
+
+(put 'cl-struct-js2-scope 'js2-visitor 'js2-visit-block)
+(put 'cl-struct-js2-scope 'js2-printer 'js2-print-none)
+
+(defun js2-node-get-enclosing-scope (node)
+  "Return the innermost `js2-scope' node surrounding NODE.
+Returns nil if there is no enclosing scope node."
+  (let ((parent (js2-node-parent node)))
+    (while (not (js2-scope-p parent))
+      (setq parent (js2-node-parent parent)))
+    parent))
+
+(defun js2-get-defining-scope (scope name)
+  "Search up scope chain from SCOPE looking for NAME, a string or symbol.
+Returns `js2-scope' in which NAME is defined, or nil if not found."
+  (let ((sym (if (symbolp name)
+                 name
+               (intern name)))
+        table
+        result
+        (continue t))
+    (while (and scope continue)
+      (if (or
+           (and (setq table (js2-scope-symbol-table scope))
+                (assq sym table))
+           (and (eq sym 'arguments)
+                (js2-function-node-p scope)))
+          (setq continue nil
+                result scope)
+        (setq scope (js2-scope-parent-scope scope))))
+    result))
+
+(defun js2-scope-get-symbol (scope name)
+  "Return symbol table entry for NAME in SCOPE.
+NAME can be a string or symbol.   Returns a `js2-symbol' or nil if not found."
+  (and (js2-scope-symbol-table scope)
+       (cdr (assq (if (symbolp name)
+                      name
+                    (intern name))
+                  (js2-scope-symbol-table scope)))))
+
+(defun js2-scope-put-symbol (scope name symbol)
+  "Enter SYMBOL into symbol-table for SCOPE under NAME.
+NAME can be a Lisp symbol or string.  SYMBOL is a `js2-symbol'."
+  (let* ((table (js2-scope-symbol-table scope))
+         (sym (if (symbolp name) name (intern name)))
+         (entry (assq sym table)))
+    (if entry
+        (setcdr entry symbol)
+      (push (cons sym symbol)
+            (js2-scope-symbol-table scope)))))
+
+(cl-defstruct (js2-symbol
+               (:constructor nil)
+               (:constructor make-js2-symbol (decl-type name &optional ast-node)))
+  "A symbol table entry."
+  ;; One of js2-FUNCTION, js2-LP (for parameters), js2-VAR,
+  ;; js2-LET, or js2-CONST
+  decl-type
+  name  ; string
+  ast-node) ; a `js2-node'
+
+(cl-defstruct (js2-error-node
+               (:include js2-node)
+               (:constructor nil) ; silence emacs21 byte-compiler
+               (:constructor make-js2-error-node (&key (type js2-ERROR)
+                                                       (pos (js2-current-token-beg))
+                                                       len)))
+  "AST node representing a parse error.")
+
+(put 'cl-struct-js2-error-node 'js2-visitor 'js2-visit-none)
+(put 'cl-struct-js2-error-node 'js2-printer 'js2-print-none)
+
+(cl-defstruct (js2-script-node
+               (:include js2-scope)
+               (:constructor nil)
+               (:constructor make-js2-script-node (&key (type js2-SCRIPT)
+                                                        (pos (js2-current-token-beg))
+                                                        len
+                                                        ;; FIXME: What are those?
+                                                        var-decls
+                                                        fun-decls)))
+  functions   ; Lisp list of nested functions
+  regexps     ; Lisp list of (string . flags)
+  symbols     ; alist (every symbol gets unique index)
+  (param-count 0)
+  var-names   ; vector of string names
+  consts      ; bool-vector matching var-decls
+  (temp-number 0))  ; for generating temp variables
+
+(put 'cl-struct-js2-script-node 'js2-visitor 'js2-visit-block)
+(put 'cl-struct-js2-script-node 'js2-printer 'js2-print-script)
+
+(defun js2-print-script (node indent)
+  (dolist (kid (js2-block-node-kids node))
+    (js2-print-ast kid indent)))
+
+(cl-defstruct (js2-ast-root
+               (:include js2-script-node)
+               (:constructor nil)
+               (:constructor make-js2-ast-root (&key (type js2-SCRIPT)
+                                                     (pos (js2-current-token-beg))
+                                                     len
+                                                     buffer)))
+  "The root node of a js2 AST."
+  buffer         ; the source buffer from which the code was parsed
+  comments       ; a Lisp list of comments, ordered by start position
+  errors         ; a Lisp list of errors found during parsing
+  warnings       ; a Lisp list of warnings found during parsing
+  node-count)    ; number of nodes in the tree, including the root
+
+(put 'cl-struct-js2-ast-root 'js2-visitor 'js2-visit-ast-root)
+(put 'cl-struct-js2-ast-root 'js2-printer 'js2-print-script)
+
+(defun js2-visit-ast-root (ast callback)
+  (dolist (kid (js2-ast-root-kids ast))
+    (js2-visit-ast kid callback))
+  (dolist (comment (js2-ast-root-comments ast))
+    (js2-visit-ast comment callback)))
+
+(cl-defstruct (js2-comment-node
+               (:include js2-node)
+               (:constructor nil)
+               (:constructor make-js2-comment-node (&key (type js2-COMMENT)
+                                                         (pos (js2-current-token-beg))
+                                                         len
+                                                         format)))
+  format)  ; 'line, 'block, 'jsdoc or 'html
+
+(put 'cl-struct-js2-comment-node 'js2-visitor 'js2-visit-none)
+(put 'cl-struct-js2-comment-node 'js2-printer 'js2-print-comment)
+
+(defun js2-print-comment (n i)
+  ;; We really ought to link end-of-line comments to their nodes.
+  ;; Or maybe we could add a new comment type, 'endline.
+  (insert (js2-make-pad i)
+          (js2-node-string n)))
+
+(cl-defstruct (js2-expr-stmt-node
+               (:include js2-node)
+               (:constructor nil)
+               (:constructor make-js2-expr-stmt-node (&key (type js2-EXPR_VOID)
+                                                           (pos js2-ts-cursor)
+                                                           len
+                                                           expr)))
+  "An expression statement."
+  expr)
+
+(defsubst js2-expr-stmt-node-set-has-result (node)
+  "Change NODE type to `js2-EXPR_RESULT'.  Used for code generation."
+  (setf (js2-node-type node) js2-EXPR_RESULT))
+
+(put 'cl-struct-js2-expr-stmt-node 'js2-visitor 'js2-visit-expr-stmt-node)
+(put 'cl-struct-js2-expr-stmt-node 'js2-printer 'js2-print-expr-stmt-node)
+
+(defun js2-visit-expr-stmt-node (n v)
+  (js2-visit-ast (js2-expr-stmt-node-expr n) v))
+
+(defun js2-print-expr-stmt-node (n indent)
+  (js2-print-ast (js2-expr-stmt-node-expr n) indent)
+  (insert ";\n"))
+
+(cl-defstruct (js2-loop-node
+               (:include js2-scope)
+               (:constructor nil))
+  "Abstract supertype of loop nodes."
+  body      ; a `js2-block-node'
+  lp        ; position of left-paren, nil if omitted
+  rp)       ; position of right-paren, nil if omitted
+
+(cl-defstruct (js2-do-node
+               (:include js2-loop-node)
+               (:constructor nil)
+               (:constructor make-js2-do-node (&key (type js2-DO)
+                                                    (pos (js2-current-token-beg))
+                                                    len
+                                                    body
+                                                    condition
+                                                    while-pos
+                                                    lp
+                                                    rp)))
+  "AST node for do-loop."
+  condition  ; while (expression)
+  while-pos) ; buffer position of 'while' keyword
+
+(put 'cl-struct-js2-do-node 'js2-visitor 'js2-visit-do-node)
+(put 'cl-struct-js2-do-node 'js2-printer 'js2-print-do-node)
+
+(defun js2-visit-do-node (n v)
+  (js2-visit-ast (js2-do-node-body n) v)
+  (js2-visit-ast (js2-do-node-condition n) v))
+
+(defun js2-print-do-node (n i)
+  (let ((pad (js2-make-pad i)))
+    (insert pad "do {\n")
+    (dolist (kid (js2-block-node-kids (js2-do-node-body n)))
+      (js2-print-ast kid (1+ i)))
+    (insert pad "} while (")
+    (js2-print-ast (js2-do-node-condition n) 0)
+    (insert ");\n")))
+
+(cl-defstruct (js2-export-node
+               (:include js2-node)
+               (:constructor nil)
+               (:constructor make-js2-export-node (&key (type js2-EXPORT)
+                                                        (pos) (js2-current-token-beg)
+                                                        len
+                                                        exports-list
+                                                        from-clause
+                                                        declaration
+                                                        default)))
+  "AST node for an export statement. There are many things that can be exported,
+so many of its properties will be nil.
+"
+  exports-list ; lisp list of js2-export-binding-node to export
+  from-clause ; js2-from-clause-node for re-exporting symbols from another module
+  declaration ; js2-var-decl-node (var, let, const) or js2-class-node
+  default) ; js2-function-node or js2-assign-node
+
+(put 'cl-struct-js2-export-node 'js2-visitor 'js2-visit-export-node)
+(put 'cl-struct-js2-export-node 'js2-printer 'js2-print-export-node)
+
+(defun js2-visit-export-node (n v)
+  (let ((exports-list (js2-export-node-exports-list n))
+        (from (js2-export-node-from-clause n))
+        (declaration (js2-export-node-declaration n))
+        (default (js2-export-node-default n)))
+    (when exports-list
+      (dolist (export exports-list)
+        (js2-visit-ast export v)))
+    (when from
+      (js2-visit-ast from v))
+    (when declaration
+      (js2-visit-ast declaration v))
+    (when default
+      (js2-visit-ast default v))))
+
+(defun js2-print-export-node (n i)
+  (let ((pad (js2-make-pad i))
+        (exports-list (js2-export-node-exports-list n))
+        (from (js2-export-node-from-clause n))
+        (declaration (js2-export-node-declaration n))
+        (default (js2-export-node-default n)))
+    (insert pad "export ")
+    (cond
+     (default
+       (insert "default ")
+       (js2-print-ast default i))
+     (declaration
+       (js2-print-ast declaration i))
+     ((and exports-list from)
+      (js2-print-named-imports exports-list)
+      (insert " ")
+      (js2-print-from-clause from))
+     (from
+      (insert "* ")
+      (js2-print-from-clause from))
+     (exports-list
+      (js2-print-named-imports exports-list)))
+    (insert ";\n")))
+
+(cl-defstruct (js2-while-node
+               (:include js2-loop-node)
+               (:constructor nil)
+               (:constructor make-js2-while-node (&key (type js2-WHILE)
+                                                       (pos (js2-current-token-beg))
+                                                       len body
+                                                       condition lp
+                                                       rp)))
+  "AST node for while-loop."
+  condition)    ; while-condition
+
+(put 'cl-struct-js2-while-node 'js2-visitor 'js2-visit-while-node)
+(put 'cl-struct-js2-while-node 'js2-printer 'js2-print-while-node)
+
+(defun js2-visit-while-node (n v)
+  (js2-visit-ast (js2-while-node-condition n) v)
+  (js2-visit-ast (js2-while-node-body n) v))
+
+(defun js2-print-while-node (n i)
+  (let ((pad (js2-make-pad i)))
+    (insert pad "while (")
+    (js2-print-ast (js2-while-node-condition n) 0)
+    (insert ") {\n")
+    (js2-print-body (js2-while-node-body n) (1+ i))
+    (insert pad "}\n")))
+
+(cl-defstruct (js2-for-node
+               (:include js2-loop-node)
+               (:constructor nil)
+               (:constructor make-js2-for-node (&key (type js2-FOR)
+                                                     (pos js2-ts-cursor)
+                                                     len body init
+                                                     condition
+                                                     update lp rp)))
+  "AST node for a C-style for-loop."
+  init       ; initialization expression
+  condition  ; loop condition
+  update)    ; update clause
+
+(put 'cl-struct-js2-for-node 'js2-visitor 'js2-visit-for-node)
+(put 'cl-struct-js2-for-node 'js2-printer 'js2-print-for-node)
+
+(defun js2-visit-for-node (n v)
+  (js2-visit-ast (js2-for-node-init n) v)
+  (js2-visit-ast (js2-for-node-condition n) v)
+  (js2-visit-ast (js2-for-node-update n) v)
+  (js2-visit-ast (js2-for-node-body n) v))
+
+(defun js2-print-for-node (n i)
+  (let ((pad (js2-make-pad i)))
+    (insert pad "for (")
+    (js2-print-ast (js2-for-node-init n) 0)
+    (insert "; ")
+    (js2-print-ast (js2-for-node-condition n) 0)
+    (insert "; ")
+    (js2-print-ast (js2-for-node-update n) 0)
+    (insert ") {\n")
+    (js2-print-body (js2-for-node-body n) (1+ i))
+    (insert pad "}\n")))
+
+(cl-defstruct (js2-for-in-node
+               (:include js2-loop-node)
+               (:constructor nil)
+               (:constructor make-js2-for-in-node (&key (type js2-FOR)
+                                                        (pos js2-ts-cursor)
+                                                        len body
+                                                        iterator
+                                                        object
+                                                        in-pos
+                                                        each-pos
+                                                        foreach-p forof-p
+                                                        lp rp)))
+  "AST node for a for..in loop."
+  iterator  ; [var] foo in ...
+  object    ; object over which we're iterating
+  in-pos    ; buffer position of 'in' keyword
+  each-pos  ; buffer position of 'each' keyword, if foreach-p
+  foreach-p ; t if it's a for-each loop
+  forof-p)  ; t if it's a for-of loop
+
+(put 'cl-struct-js2-for-in-node 'js2-visitor 'js2-visit-for-in-node)
+(put 'cl-struct-js2-for-in-node 'js2-printer 'js2-print-for-in-node)
+
+(defun js2-visit-for-in-node (n v)
+  (js2-visit-ast (js2-for-in-node-iterator n) v)
+  (js2-visit-ast (js2-for-in-node-object n) v)
+  (js2-visit-ast (js2-for-in-node-body n) v))
+
+(defun js2-print-for-in-node (n i)
+  (let ((pad (js2-make-pad i))
+        (foreach (js2-for-in-node-foreach-p n))
+        (forof (js2-for-in-node-forof-p n)))
+    (insert pad "for ")
+    (if foreach
+        (insert "each "))
+    (insert "(")
+    (js2-print-ast (js2-for-in-node-iterator n) 0)
+    (insert (if forof " of " " in "))
+    (js2-print-ast (js2-for-in-node-object n) 0)
+    (insert ") {\n")
+    (js2-print-body (js2-for-in-node-body n) (1+ i))
+    (insert pad "}\n")))
+
+(cl-defstruct (js2-return-node
+               (:include js2-node)
+               (:constructor nil)
+               (:constructor make-js2-return-node (&key (type js2-RETURN)
+                                                        (pos js2-ts-cursor)
+                                                        len
+                                                        retval)))
+  "AST node for a return statement."
+  retval)  ; expression to return, or 'undefined
+
+(put 'cl-struct-js2-return-node 'js2-visitor 'js2-visit-return-node)
+(put 'cl-struct-js2-return-node 'js2-printer 'js2-print-return-node)
+
+(defun js2-visit-return-node (n v)
+  (js2-visit-ast (js2-return-node-retval n) v))
+
+(defun js2-print-return-node (n i)
+  (insert (js2-make-pad i) "return")
+  (when (js2-return-node-retval n)
+    (insert " ")
+    (js2-print-ast (js2-return-node-retval n) 0))
+  (insert ";\n"))
+
+(cl-defstruct (js2-if-node
+               (:include js2-node)
+               (:constructor nil)
+               (:constructor make-js2-if-node (&key (type js2-IF)
+                                                    (pos js2-ts-cursor)
+                                                    len condition
+                                                    then-part
+                                                    else-pos
+                                                    else-part lp
+                                                    rp)))
+  "AST node for an if-statement."
+  condition   ; expression
+  then-part   ; statement or block
+  else-pos    ; optional buffer position of 'else' keyword
+  else-part   ; optional statement or block
+  lp          ; position of left-paren, nil if omitted
+  rp)         ; position of right-paren, nil if omitted
+
+(put 'cl-struct-js2-if-node 'js2-visitor 'js2-visit-if-node)
+(put 'cl-struct-js2-if-node 'js2-printer 'js2-print-if-node)
+
+(defun js2-visit-if-node (n v)
+  (js2-visit-ast (js2-if-node-condition n) v)
+  (js2-visit-ast (js2-if-node-then-part n) v)
+  (js2-visit-ast (js2-if-node-else-part n) v))
+
+(defun js2-print-if-node (n i)
+  (let ((pad (js2-make-pad i))
+        (then-part (js2-if-node-then-part n))
+        (else-part (js2-if-node-else-part n)))
+    (insert pad "if (")
+    (js2-print-ast (js2-if-node-condition n) 0)
+    (insert ") {\n")
+    (js2-print-body then-part (1+ i))
+    (insert pad "}")
+    (cond
+     ((not else-part)
+      (insert "\n"))
+     ((js2-if-node-p else-part)
+      (insert " else ")
+      (js2-print-body else-part i))
+     (t
+      (insert " else {\n")
+      (js2-print-body else-part (1+ i))
+      (insert pad "}\n")))))
+
+(cl-defstruct (js2-export-binding-node
+               (:include js2-node)
+               (:constructor nil)
+               (:constructor make-js2-export-binding-node (&key (type -1)
+                                                                pos
+                                                                len
+                                                                local-name
+                                                                extern-name)))
+  "AST node for an external symbol binding.
+It contains a local-name node which is the name of the value in the
+current scope, and extern-name which is the name of the value in the
+imported or exported scope. By default these are the same, but if the
+name is aliased as in {foo as bar}, it would have an extern-name node
+containing 'foo' and a local-name node containing 'bar'."
+  local-name ; js2-name-node with the variable name in this scope
+  extern-name)   ; js2-name-node with the value name in the exporting module
+
+(put 'cl-struct-js2-export-binding-node 'js2-printer 'js2-print-extern-binding)
+(put 'cl-struct-js2-export-binding-node 'js2-visitor 'js2-visit-extern-binding)
+
+(defun js2-visit-extern-binding (n v)
+  "Visit an extern binding node. First visit the local-name, and, if
+different, visit the extern-name."
+  (let ((local-name (js2-export-binding-node-local-name n))
+        (extern-name (js2-export-binding-node-extern-name n)))
+    (when local-name
+      (js2-visit-ast local-name v))
+    (when (not (equal local-name extern-name))
+      (js2-visit-ast extern-name v))))
+
+(defun js2-print-extern-binding (n i)
+  "Print a representation of a single extern binding. E.g. 'foo' or
+'foo as bar'."
+  (let ((local-name (js2-export-binding-node-local-name n))
+        (extern-name (js2-export-binding-node-extern-name n)))
+    (insert (js2-name-node-name extern-name))
+    (when (not (equal local-name extern-name))
+      (insert " as ")
+      (insert (js2-name-node-name local-name)))))
+
+
+(cl-defstruct (js2-import-node
+               (:include js2-node)
+               (:constructor nil)
+               (:constructor make-js2-import-node (&key (type js2-IMPORT)
+                                                        (pos (js2-current-token-beg))
+                                                        len
+                                                        import
+                                                        from
+                                                        module-id)))
+  "AST node for an import statement. It follows the form
+
+import ModuleSpecifier;
+import ImportClause FromClause;"
+  import     ; js2-import-clause-node specifying which names are to imported.
+  from       ; js2-from-clause-node indicating the module from which to import.
+  module-id) ; module-id of the import. E.g. 'src/mylib'.
+
+(put 'cl-struct-js2-import-node 'js2-printer 'js2-print-import)
+(put 'cl-struct-js2-import-node 'js2-visitor 'js2-visit-import)
+
+(defun js2-visit-import (n v)
+  (let ((import-clause (js2-import-node-import n))
+        (from-clause (js2-import-node-from n)))
+    (when import-clause
+      (js2-visit-ast import-clause v))
+    (when from-clause
+      (js2-visit-ast from-clause v))))
+
+(defun js2-print-import (n i)
+  "Prints a representation of the import node"
+  (let ((pad (js2-make-pad i))
+        (import-clause (js2-import-node-import n))
+        (from-clause (js2-import-node-from n))
+        (module-id (js2-import-node-module-id n)))
+    (insert pad "import ")
+    (if import-clause
+        (progn
+          (js2-print-import-clause import-clause)
+          (insert " ")
+          (js2-print-from-clause from-clause))
+      (insert "'")
+      (insert module-id)
+      (insert "'"))
+    (insert ";\n")))
+
+(cl-defstruct (js2-import-clause-node
+               (:include js2-node)
+               (:constructor nil)
+               (:constructor make-js2-import-clause-node (&key (type -1)
+                                                               pos
+                                                               len
+                                                               namespace-import
+                                                               named-imports
+                                                               default-binding)))
+  "AST node corresponding to the import clause of an import statement. This is
+the portion of the import that bindings names from the external context to the
+local context."
+  namespace-import ; js2-namespace-import-node. E.g. '* as lib'
+  named-imports    ; lisp list of js2-export-binding-node for all named imports.
+  default-binding) ; js2-export-binding-node for the default import binding
+
+(put 'cl-struct-js2-import-clause-node 'js2-visitor 'js2-visit-import-clause)
+(put 'cl-struct-js2-import-clause-node 'js2-printer 'js2-print-import-clause)
+
+(defun js2-visit-import-clause (n v)
+  (let ((ns-import (js2-import-clause-node-namespace-import n))
+        (named-imports (js2-import-clause-node-named-imports n))
+        (default (js2-import-clause-node-default-binding n)))
+    (when ns-import
+      (js2-visit-ast ns-import v))
+    (when named-imports
+      (dolist (import named-imports)
+        (js2-visit-ast import v)))
+    (when default
+      (js2-visit-ast default v))))
+
+(defun js2-print-import-clause (n)
+  (let ((ns-import (js2-import-clause-node-namespace-import n))
+        (named-imports (js2-import-clause-node-named-imports n))
+        (default (js2-import-clause-node-default-binding n)))
+    (cond
+     ((and default ns-import)
+      (js2-print-ast default)
+      (insert ", ")
+      (js2-print-namespace-import ns-import))
+     ((and default named-imports)
+      (js2-print-ast default)
+      (insert ", ")
+      (js2-print-named-imports named-imports))
+     (default
+      (js2-print-ast default))
+     (ns-import
+      (js2-print-namespace-import ns-import))
+     (named-imports
+      (js2-print-named-imports named-imports)))))
+
+(defun js2-print-namespace-import (node)
+  (insert "* as ")
+  (insert (js2-name-node-name (js2-namespace-import-node-name node))))
+
+(defun js2-print-named-imports (imports)
+  (insert "{")
+  (let ((len (length imports))
+        (n 0))
+    (while (< n len)
+      (js2-print-extern-binding (nth n imports) 0)
+      (unless (= n (- len 1))
+        (insert ", "))
+      (setq n (+ n 1))))
+  (insert "}"))
+
+(cl-defstruct (js2-namespace-import-node
+               (:include js2-node)
+               (:constructor nil)
+               (:constructor make-js2-namespace-import-node (&key (type -1)
+                                                                  pos
+                                                                  len
+                                                                  name)))
+  "AST node for a complete namespace import.
+E.g. the '* as lib' expression in:
+
+import * as lib from 'src/lib'
+
+It contains a single name node referring to the bound name."
+  name) ; js2-name-node of the bound name.
+
+(defun js2-visit-namespace-import (n v)
+  (js2-visit-ast (js2-namespace-import-node-name n) v))
+
+(put 'cl-struct-js2-namespace-import-node 'js2-visitor 'js2-visit-namespace-import)
+(put 'cl-struct-js2-namespace-import-node 'js2-printer 'js2-print-namespace-import)
+
+(cl-defstruct (js2-from-clause-node
+               (:include js2-node)
+               (:constructor nil)
+               (:constructor make-js2-from-clause-node (&key (type js2-NAME)
+                                                             pos
+                                                             len
+                                                             module-id
+                                                             metadata-p)))
+  "AST node for the from clause in an import or export statement.
+E.g. from 'my/module'. It can refere to either an external module, or to the
+modules metadata itself."
+  module-id ; string containing the module specifier.
+  metadata-p) ; true if this clause refers to the module's metadata
+
+(put 'cl-struct-js2-from-clause-node 'js2-visitor 'js2-visit-none)
+(put 'cl-struct-js2-from-clause-node 'js2-printer 'js2-print-from-clause)
+
+(defun js2-print-from-clause (n)
+  (insert "from ")
+  (if (js2-from-clause-node-metadata-p n)
+      (insert "this module")
+    (insert "'")
+    (insert (js2-from-clause-node-module-id n))
+    (insert "'")))
+
+(cl-defstruct (js2-try-node
+               (:include js2-node)
+               (:constructor nil)
+               (:constructor make-js2-try-node (&key (type js2-TRY)
+                                                     (pos js2-ts-cursor)
+                                                     len
+                                                     try-block
+                                                     catch-clauses
+                                                     finally-block)))
+  "AST node for a try-statement."
+  try-block
+  catch-clauses  ; a Lisp list of `js2-catch-node'
+  finally-block) ; a `js2-finally-node'
+
+(put 'cl-struct-js2-try-node 'js2-visitor 'js2-visit-try-node)
+(put 'cl-struct-js2-try-node 'js2-printer 'js2-print-try-node)
+
+(defun js2-visit-try-node (n v)
+  (js2-visit-ast (js2-try-node-try-block n) v)
+  (dolist (clause (js2-try-node-catch-clauses n))
+    (js2-visit-ast clause v))
+  (js2-visit-ast (js2-try-node-finally-block n) v))
+
+(defun js2-print-try-node (n i)
+  (let ((pad (js2-make-pad i))
+        (catches (js2-try-node-catch-clauses n))
+        (finally (js2-try-node-finally-block n)))
+    (insert pad "try {\n")
+    (js2-print-body (js2-try-node-try-block n) (1+ i))
+    (insert pad "}")
+    (when catches
+      (dolist (catch catches)
+        (js2-print-ast catch i)))
+    (if finally
+        (js2-print-ast finally i)
+      (insert "\n"))))
+
+(cl-defstruct (js2-catch-node
+               (:include js2-scope)
+               (:constructor nil)
+               (:constructor make-js2-catch-node (&key (type js2-CATCH)
+                                                       (pos js2-ts-cursor)
+                                                       len
+                                                       param
+                                                       guard-kwd
+                                                       guard-expr
+                                                       lp rp)))
+  "AST node for a catch clause."
+  param       ; destructuring form or simple name node
+  guard-kwd   ; relative buffer position of "if" in "catch (x if ...)"
+  guard-expr  ; catch condition, a `js2-node'
+  lp          ; buffer position of left-paren, nil if omitted
+  rp)         ; buffer position of right-paren, nil if omitted
+
+(put 'cl-struct-js2-catch-node 'js2-visitor 'js2-visit-catch-node)
+(put 'cl-struct-js2-catch-node 'js2-printer 'js2-print-catch-node)
+
+(defun js2-visit-catch-node (n v)
+  (js2-visit-ast (js2-catch-node-param n) v)
+  (when (js2-catch-node-guard-kwd n)
+    (js2-visit-ast (js2-catch-node-guard-expr n) v))
+  (js2-visit-block n v))
+
+(defun js2-print-catch-node (n i)
+  (let ((pad (js2-make-pad i))
+        (guard-kwd (js2-catch-node-guard-kwd n))
+        (guard-expr (js2-catch-node-guard-expr n)))
+    (insert " catch (")
+    (js2-print-ast (js2-catch-node-param n) 0)
+    (when guard-kwd
+      (insert " if ")
+      (js2-print-ast guard-expr 0))
+    (insert ") {\n")
+    (js2-print-body n (1+ i))
+    (insert pad "}")))
+
+(cl-defstruct (js2-finally-node
+               (:include js2-node)
+               (:constructor nil)
+               (:constructor make-js2-finally-node (&key (type js2-FINALLY)
+                                                         (pos js2-ts-cursor)
+                                                         len body)))
+  "AST node for a finally clause."
+  body)  ; a `js2-node', often but not always a block node
+
+(put 'cl-struct-js2-finally-node 'js2-visitor 'js2-visit-finally-node)
+(put 'cl-struct-js2-finally-node 'js2-printer 'js2-print-finally-node)
+
+(defun js2-visit-finally-node (n v)
+  (js2-visit-ast (js2-finally-node-body n) v))
+
+(defun js2-print-finally-node (n i)
+  (let ((pad (js2-make-pad i)))
+    (insert " finally {\n")
+    (js2-print-body (js2-finally-node-body n) (1+ i))
+    (insert pad "}\n")))
+
+(cl-defstruct (js2-switch-node
+               (:include js2-node)
+               (:constructor nil)
+               (:constructor make-js2-switch-node (&key (type js2-SWITCH)
+                                                        (pos js2-ts-cursor)
+                                                        len
+                                                        discriminant
+                                                        cases lp
+                                                        rp)))
+  "AST node for a switch statement."
+  discriminant  ; a `js2-node' (switch expression)
+  cases  ; a Lisp list of `js2-case-node'
+  lp     ; position of open-paren for discriminant, nil if omitted
+  rp)    ; position of close-paren for discriminant, nil if omitted
+
+(put 'cl-struct-js2-switch-node 'js2-visitor 'js2-visit-switch-node)
+(put 'cl-struct-js2-switch-node 'js2-printer 'js2-print-switch-node)
+
+(defun js2-visit-switch-node (n v)
+  (js2-visit-ast (js2-switch-node-discriminant n) v)
+  (dolist (c (js2-switch-node-cases n))
+    (js2-visit-ast c v)))
+
+(defun js2-print-switch-node (n i)
+  (let ((pad (js2-make-pad i))
+        (cases (js2-switch-node-cases n)))
+    (insert pad "switch (")
+    (js2-print-ast (js2-switch-node-discriminant n) 0)
+    (insert ") {\n")
+    (dolist (case cases)
+      (js2-print-ast case i))
+    (insert pad "}\n")))
+
+(cl-defstruct (js2-case-node
+               (:include js2-block-node)
+               (:constructor nil)
+               (:constructor make-js2-case-node (&key (type js2-CASE)
+                                                      (pos js2-ts-cursor)
+                                                      len kids expr)))
+  "AST node for a case clause of a switch statement."
+  expr)   ; the case expression (nil for default)
+
+(put 'cl-struct-js2-case-node 'js2-visitor 'js2-visit-case-node)
+(put 'cl-struct-js2-case-node 'js2-printer 'js2-print-case-node)
+
+(defun js2-visit-case-node (n v)
+  (js2-visit-ast (js2-case-node-expr n) v)
+  (js2-visit-block n v))
+
+(defun js2-print-case-node (n i)
+  (let ((pad (js2-make-pad i))
+        (expr (js2-case-node-expr n)))
+    (insert pad)
+    (if (null expr)
+        (insert "default:\n")
+      (insert "case ")
+      (js2-print-ast expr 0)
+      (insert ":\n"))
+    (dolist (kid (js2-case-node-kids n))
+      (js2-print-ast kid (1+ i)))))
+
+(cl-defstruct (js2-throw-node
+               (:include js2-node)
+               (:constructor nil)
+               (:constructor make-js2-throw-node (&key (type js2-THROW)
+                                                       (pos js2-ts-cursor)
+                                                       len expr)))
+  "AST node for a throw statement."
+  expr)   ; the expression to throw
+
+(put 'cl-struct-js2-throw-node 'js2-visitor 'js2-visit-throw-node)
+(put 'cl-struct-js2-throw-node 'js2-printer 'js2-print-throw-node)
+
+(defun js2-visit-throw-node (n v)
+  (js2-visit-ast (js2-throw-node-expr n) v))
+
+(defun js2-print-throw-node (n i)
+  (insert (js2-make-pad i) "throw ")
+  (js2-print-ast (js2-throw-node-expr n) 0)
+  (insert ";\n"))
+
+(cl-defstruct (js2-with-node
+               (:include js2-node)
+               (:constructor nil)
+               (:constructor make-js2-with-node (&key (type js2-WITH)
+                                                      (pos js2-ts-cursor)
+                                                      len object
+                                                      body lp rp)))
+  "AST node for a with-statement."
+  object
+  body
+  lp    ; buffer position of left-paren around object, nil if omitted
+  rp)   ; buffer position of right-paren around object, nil if omitted
+
+(put 'cl-struct-js2-with-node 'js2-visitor 'js2-visit-with-node)
+(put 'cl-struct-js2-with-node 'js2-printer 'js2-print-with-node)
+
+(defun js2-visit-with-node (n v)
+  (js2-visit-ast (js2-with-node-object n) v)
+  (js2-visit-ast (js2-with-node-body n) v))
+
+(defun js2-print-with-node (n i)
+  (let ((pad (js2-make-pad i)))
+    (insert pad "with (")
+    (js2-print-ast (js2-with-node-object n) 0)
+    (insert ") {\n")
+    (js2-print-body (js2-with-node-body n) (1+ i))
+    (insert pad "}\n")))
+
+(cl-defstruct (js2-label-node
+               (:include js2-node)
+               (:constructor nil)
+               (:constructor make-js2-label-node (&key (type js2-LABEL)
+                                                       (pos js2-ts-cursor)
+                                                       len name)))
+  "AST node for a statement label or case label."
+  name   ; a string
+  loop)  ; for validating and code-generating continue-to-label
+
+(put 'cl-struct-js2-label-node 'js2-visitor 'js2-visit-none)
+(put 'cl-struct-js2-label-node 'js2-printer 'js2-print-label)
+
+(defun js2-print-label (n i)
+  (insert (js2-make-pad i)
+          (js2-label-node-name n)
+          ":\n"))
+
+(cl-defstruct (js2-labeled-stmt-node
+               (:include js2-node)
+               (:constructor nil)
+               ;; type needs to be in `js2-side-effecting-tokens' to avoid spurious
+               ;; no-side-effects warnings, hence js2-EXPR_RESULT.
+               (:constructor make-js2-labeled-stmt-node (&key (type js2-EXPR_RESULT)
+                                                              (pos js2-ts-cursor)
+                                                              len labels stmt)))
+  "AST node for a statement with one or more labels.
+Multiple labels for a statement are collapsed into the labels field."
+  labels  ; Lisp list of `js2-label-node'
+  stmt)   ; the statement these labels are for
+
+(put 'cl-struct-js2-labeled-stmt-node 'js2-visitor 'js2-visit-labeled-stmt)
+(put 'cl-struct-js2-labeled-stmt-node 'js2-printer 'js2-print-labeled-stmt)
+
+(defun js2-get-label-by-name (lbl-stmt name)
+  "Return a `js2-label-node' by NAME from LBL-STMT's labels list.
+Returns nil if no such label is in the list."
+  (let ((label-list (js2-labeled-stmt-node-labels lbl-stmt))
+        result)
+    (while (and label-list (not result))
+      (if (string= (js2-label-node-name (car label-list)) name)
+          (setq result (car label-list))
+        (setq label-list (cdr label-list))))
+    result))
+
+(defun js2-visit-labeled-stmt (n v)
+  (dolist (label (js2-labeled-stmt-node-labels n))
+    (js2-visit-ast label v))
+  (js2-visit-ast (js2-labeled-stmt-node-stmt n) v))
+
+(defun js2-print-labeled-stmt (n i)
+  (dolist (label (js2-labeled-stmt-node-labels n))
+    (js2-print-ast label i))
+  (js2-print-ast (js2-labeled-stmt-node-stmt n) i))
+
+(defun js2-labeled-stmt-node-contains (node label)
+  "Return t if NODE contains LABEL in its label set.
+NODE is a `js2-labels-node'.  LABEL is an identifier."
+  (cl-loop for nl in (js2-labeled-stmt-node-labels node)
+           if (string= label (js2-label-node-name nl))
+           return t
+           finally return nil))
+
+(defsubst js2-labeled-stmt-node-add-label (node label)
+  "Add a `js2-label-node' to the label set for this statement."
+  (setf (js2-labeled-stmt-node-labels node)
+        (nconc (js2-labeled-stmt-node-labels node) (list label))))
+
+(cl-defstruct (js2-jump-node
+               (:include js2-node)
+               (:constructor nil))
+  "Abstract supertype of break and continue nodes."
+  label   ; `js2-name-node' for location of label identifier, if present
+  target) ; target js2-labels-node or loop/switch statement
+
+(defun js2-visit-jump-node (n v)
+  ;; We don't visit the target, since it's a back-link.
+  (js2-visit-ast (js2-jump-node-label n) v))
+
+(cl-defstruct (js2-break-node
+               (:include js2-jump-node)
+               (:constructor nil)
+               (:constructor make-js2-break-node (&key (type js2-BREAK)
+                                                       (pos js2-ts-cursor)
+                                                       len label target)))
+  "AST node for a break statement.
+The label field is a `js2-name-node', possibly nil, for the named label
+if provided.  E.g. in 'break foo', it represents 'foo'.  The target field
+is the target of the break - a label node or enclosing loop/switch statement.")
+
+(put 'cl-struct-js2-break-node 'js2-visitor 'js2-visit-jump-node)
+(put 'cl-struct-js2-break-node 'js2-printer 'js2-print-break-node)
+
+(defun js2-print-break-node (n i)
+  (insert (js2-make-pad i) "break")
+  (when (js2-break-node-label n)
+    (insert " ")
+    (js2-print-ast (js2-break-node-label n) 0))
+  (insert ";\n"))
+
+(cl-defstruct (js2-continue-node
+               (:include js2-jump-node)
+               (:constructor nil)
+               (:constructor make-js2-continue-node (&key (type js2-CONTINUE)
+                                                          (pos js2-ts-cursor)
+                                                          len label target)))
+  "AST node for a continue statement.
+The label field is the user-supplied enclosing label name, a `js2-name-node'.
+It is nil if continue specifies no label.  The target field is the jump target:
+a `js2-label-node' or the innermost enclosing loop.")
+
+(put 'cl-struct-js2-continue-node 'js2-visitor 'js2-visit-jump-node)
+(put 'cl-struct-js2-continue-node 'js2-printer 'js2-print-continue-node)
+
+(defun js2-print-continue-node (n i)
+  (insert (js2-make-pad i) "continue")
+  (when (js2-continue-node-label n)
+    (insert " ")
+    (js2-print-ast (js2-continue-node-label n) 0))
+  (insert ";\n"))
+
+(cl-defstruct (js2-function-node
+               (:include js2-script-node)
+               (:constructor nil)
+               (:constructor make-js2-function-node (&key (type js2-FUNCTION)
+                                                          (pos js2-ts-cursor)
+                                                          len
+                                                          (ftype 'FUNCTION)
+                                                          (form 'FUNCTION_STATEMENT)
+                                                          (name "")
+                                                          params rest-p
+                                                          body
+                                                          generator-type
+                                                          lp rp)))
+  "AST node for a function declaration.
+The `params' field is a Lisp list of nodes.  Each node is either a simple
+`js2-name-node', or if it's a destructuring-assignment parameter, a
+`js2-array-node' or `js2-object-node'."
+  ftype            ; FUNCTION, GETTER or SETTER
+  form             ; FUNCTION_{STATEMENT|EXPRESSION|ARROW}
+  name             ; function name (a `js2-name-node', or nil if anonymous)
+  params           ; a Lisp list of destructuring forms or simple name nodes
+  rest-p           ; if t, the last parameter is rest parameter
+  body             ; a `js2-block-node' or expression node (1.8 only)
+  lp               ; position of arg-list open-paren, or nil if omitted
+  rp               ; position of arg-list close-paren, or nil if omitted
+  ignore-dynamic   ; ignore value of the dynamic-scope flag (interpreter only)
+  needs-activation ; t if we need an activation object for this frame
+  generator-type   ; STAR, LEGACY, COMPREHENSION or nil
+  member-expr)     ; nonstandard Ecma extension from Rhino
+
+(put 'cl-struct-js2-function-node 'js2-visitor 'js2-visit-function-node)
+(put 'cl-struct-js2-function-node 'js2-printer 'js2-print-function-node)
+
+(defun js2-visit-function-node (n v)
+  (js2-visit-ast (js2-function-node-name n) v)
+  (dolist (p (js2-function-node-params n))
+    (js2-visit-ast p v))
+  (js2-visit-ast (js2-function-node-body n) v))
+
+(defun js2-print-function-node (n i)
+  (let* ((pad (js2-make-pad i))
+         (getter (js2-node-get-prop n 'GETTER_SETTER))
+         (name (or (js2-function-node-name n)
+                   (js2-function-node-member-expr n)))
+         (params (js2-function-node-params n))
+         (arrow (eq (js2-function-node-form n) 'FUNCTION_ARROW))
+         (rest-p (js2-function-node-rest-p n))
+         (body (js2-function-node-body n))
+         (expr (not (eq (js2-function-node-form n) 'FUNCTION_STATEMENT))))
+    (unless (or getter arrow)
+      (insert pad "function")
+      (when (eq (js2-function-node-generator-type n) 'STAR)
+        (insert "*")))
+    (when name
+      (insert " ")
+      (js2-print-ast name 0))
+    (insert "(")
+    (cl-loop with len = (length params)
+             for param in params
+             for count from 1
+             do
+             (when (and rest-p (= count len))
+               (insert "..."))
+             (js2-print-ast param 0)
+             (when (< count len)
+               (insert ", ")))
+    (insert ") ")
+    (when arrow
+      (insert "=> "))
+    (insert "{")
+    ;; TODO:  fix this to be smarter about indenting, etc.
+    (unless expr
+      (insert "\n"))
+    (if (js2-block-node-p body)
+        (js2-print-body body (1+ i))
+      (js2-print-ast body 0))
+    (insert pad "}")
+    (unless expr
+      (insert "\n"))))
+
+(defun js2-function-name (node)
+  "Return function name for NODE, a `js2-function-node', or nil if anonymous."
+  (and (js2-function-node-name node)
+       (js2-name-node-name (js2-function-node-name node))))
+
+;; Having this be an expression node makes it more flexible.
+;; There are IDE contexts, such as indentation in a for-loop initializer,
+;; that work better if you assume it's an expression.  Whenever we have
+;; a standalone var/const declaration, we just wrap with an expr stmt.
+;; Eclipse apparently screwed this up and now has two versions, expr and stmt.
+(cl-defstruct (js2-var-decl-node
+               (:include js2-node)
+               (:constructor nil)
+               (:constructor make-js2-var-decl-node (&key (type js2-VAR)
+                                                          (pos (js2-current-token-beg))
+                                                          len kids
+                                                          decl-type)))
+  "AST node for a variable declaration list (VAR, CONST or LET).
+The node bounds differ depending on the declaration type.  For VAR or
+CONST declarations, the bounds include the var/const keyword.  For LET
+declarations, the node begins at the position of the first child."
+  kids        ; a Lisp list of `js2-var-init-node' structs.
+  decl-type)  ; js2-VAR, js2-CONST or js2-LET
+
+(put 'cl-struct-js2-var-decl-node 'js2-visitor 'js2-visit-var-decl)
+(put 'cl-struct-js2-var-decl-node 'js2-printer 'js2-print-var-decl)
+
+(defun js2-visit-var-decl (n v)
+  (dolist (kid (js2-var-decl-node-kids n))
+    (js2-visit-ast kid v)))
+
+(defun js2-print-var-decl (n i)
+  (let ((pad (js2-make-pad i))
+        (tt (js2-var-decl-node-decl-type n)))
+    (insert pad)
+    (insert (cond
+             ((= tt js2-VAR) "var ")
+             ((= tt js2-LET) "let ")
+             ((= tt js2-CONST) "const ")
+             (t
+              (error "malformed var-decl node"))))
+    (cl-loop with kids = (js2-var-decl-node-kids n)
+             with len = (length kids)
+             for kid in kids
+             for count from 1
+             do
+             (js2-print-ast kid 0)
+             (if (< count len)
+                 (insert ", ")))))
+
+(cl-defstruct (js2-var-init-node
+               (:include js2-node)
+               (:constructor nil)
+               (:constructor make-js2-var-init-node (&key (type js2-VAR)
+                                                          (pos js2-ts-cursor)
+                                                          len target
+                                                          initializer)))
+  "AST node for a variable declaration.
+The type field will be js2-CONST for a const decl."
+  target        ; `js2-name-node', `js2-object-node', or `js2-array-node'
+  initializer)  ; initializer expression, a `js2-node'
+
+(put 'cl-struct-js2-var-init-node 'js2-visitor 'js2-visit-var-init-node)
+(put 'cl-struct-js2-var-init-node 'js2-printer 'js2-print-var-init-node)
+
+(defun js2-visit-var-init-node (n v)
+  (js2-visit-ast (js2-var-init-node-target n) v)
+  (js2-visit-ast (js2-var-init-node-initializer n) v))
+
+(defun js2-print-var-init-node (n i)
+  (let ((pad (js2-make-pad i))
+        (name (js2-var-init-node-target n))
+        (init (js2-var-init-node-initializer n)))
+    (insert pad)
+    (js2-print-ast name 0)
+    (when init
+      (insert " = ")
+      (js2-print-ast init 0))))
+
+(cl-defstruct (js2-cond-node
+               (:include js2-node)
+               (:constructor nil)
+               (:constructor make-js2-cond-node (&key (type js2-HOOK)
+                                                      (pos js2-ts-cursor)
+                                                      len
+                                                      test-expr
+                                                      true-expr
+                                                      false-expr
+                                                      q-pos c-pos)))
+  "AST node for the ternary operator"
+  test-expr
+  true-expr
+  false-expr
+  q-pos   ; buffer position of ?
+  c-pos)  ; buffer position of :
+
+(put 'cl-struct-js2-cond-node 'js2-visitor 'js2-visit-cond-node)
+(put 'cl-struct-js2-cond-node 'js2-printer 'js2-print-cond-node)
+
+(defun js2-visit-cond-node (n v)
+  (js2-visit-ast (js2-cond-node-test-expr n) v)
+  (js2-visit-ast (js2-cond-node-true-expr n) v)
+  (js2-visit-ast (js2-cond-node-false-expr n) v))
+
+(defun js2-print-cond-node (n i)
+  (let ((pad (js2-make-pad i)))
+    (insert pad)
+    (js2-print-ast (js2-cond-node-test-expr n) 0)
+    (insert " ? ")
+    (js2-print-ast (js2-cond-node-true-expr n) 0)
+    (insert " : ")
+    (js2-print-ast (js2-cond-node-false-expr n) 0)))
+
+(cl-defstruct (js2-infix-node
+               (:include js2-node)
+               (:constructor nil)
+               (:constructor make-js2-infix-node (&key type
+                                                       (pos js2-ts-cursor)
+                                                       len op-pos
+                                                       left right)))
+  "Represents infix expressions.
+Includes assignment ops like `|=', and the comma operator.
+The type field inherited from `js2-node' holds the operator."
+  op-pos    ; buffer position where operator begins
+  left      ; any `js2-node'
+  right)    ; any `js2-node'
+
+(put 'cl-struct-js2-infix-node 'js2-visitor 'js2-visit-infix-node)
+(put 'cl-struct-js2-infix-node 'js2-printer 'js2-print-infix-node)
+
+(defun js2-visit-infix-node (n v)
+  (js2-visit-ast (js2-infix-node-left n) v)
+  (js2-visit-ast (js2-infix-node-right n) v))
+
+(defconst js2-operator-tokens
+  (let ((table (make-hash-table :test 'eq))
+        (tokens
+         (list (cons js2-IN "in")
+               (cons js2-TYPEOF "typeof")
+               (cons js2-INSTANCEOF "instanceof")
+               (cons js2-DELPROP "delete")
+               (cons js2-COMMA ",")
+               (cons js2-COLON ":")
+               (cons js2-OR "||")
+               (cons js2-AND "&&")
+               (cons js2-INC "++")
+               (cons js2-DEC "--")
+               (cons js2-BITOR "|")
+               (cons js2-BITXOR "^")
+               (cons js2-BITAND "&")
+               (cons js2-EQ "==")
+               (cons js2-NE "!=")
+               (cons js2-LT "<")
+               (cons js2-LE "<=")
+               (cons js2-GT ">")
+               (cons js2-GE ">=")
+               (cons js2-LSH "<<")
+               (cons js2-RSH ">>")
+               (cons js2-URSH ">>>")
+               (cons js2-ADD "+")       ; infix plus
+               (cons js2-SUB "-")       ; infix minus
+               (cons js2-MUL "*")
+               (cons js2-DIV "/")
+               (cons js2-MOD "%")
+               (cons js2-NOT "!")
+               (cons js2-BITNOT "~")
+               (cons js2-POS "+")       ; unary plus
+               (cons js2-NEG "-")       ; unary minus
+               (cons js2-TRIPLEDOT "...")
+               (cons js2-SHEQ "===")    ; shallow equality
+               (cons js2-SHNE "!==")    ; shallow inequality
+               (cons js2-ASSIGN "=")
+               (cons js2-ASSIGN_BITOR "|=")
+               (cons js2-ASSIGN_BITXOR "^=")
+               (cons js2-ASSIGN_BITAND "&=")
+               (cons js2-ASSIGN_LSH "<<=")
+               (cons js2-ASSIGN_RSH ">>=")
+               (cons js2-ASSIGN_URSH ">>>=")
+               (cons js2-ASSIGN_ADD "+=")
+               (cons js2-ASSIGN_SUB "-=")
+               (cons js2-ASSIGN_MUL "*=")
+               (cons js2-ASSIGN_DIV "/=")
+               (cons js2-ASSIGN_MOD "%="))))
+    (cl-loop for (k . v) in tokens do
+             (puthash k v table))
+    table))
+
+(defun js2-print-infix-node (n i)
+  (let* ((tt (js2-node-type n))
+         (op (gethash tt js2-operator-tokens)))
+    (unless op
+      (error "unrecognized infix operator %s" (js2-node-type n)))
+    (insert (js2-make-pad i))
+    (js2-print-ast (js2-infix-node-left n) 0)
+    (unless (= tt js2-COMMA)
+      (insert " "))
+    (insert op)
+    (insert " ")
+    (js2-print-ast (js2-infix-node-right n) 0)))
+
+(cl-defstruct (js2-assign-node
+               (:include js2-infix-node)
+               (:constructor nil)
+               (:constructor make-js2-assign-node (&key type
+                                                        (pos js2-ts-cursor)
+                                                        len op-pos
+                                                        left right)))
+  "Represents any assignment.
+The type field holds the actual assignment operator.")
+
+(put 'cl-struct-js2-assign-node 'js2-visitor 'js2-visit-infix-node)
+(put 'cl-struct-js2-assign-node 'js2-printer 'js2-print-infix-node)
+
+(cl-defstruct (js2-unary-node
+               (:include js2-node)
+               (:constructor nil)
+               (:constructor make-js2-unary-node (&key type ; required
+                                                       (pos js2-ts-cursor)
+                                                       len operand)))
+  "AST node type for unary operator nodes.
+The type field can be NOT, BITNOT, POS, NEG, INC, DEC,
+TYPEOF, DELPROP or TRIPLEDOT.  For INC or DEC, a 'postfix node
+property is added if the operator follows the operand."
+  operand)  ; a `js2-node' expression
+
+(put 'cl-struct-js2-unary-node 'js2-visitor 'js2-visit-unary-node)
+(put 'cl-struct-js2-unary-node 'js2-printer 'js2-print-unary-node)
+
+(defun js2-visit-unary-node (n v)
+  (js2-visit-ast (js2-unary-node-operand n) v))
+
+(defun js2-print-unary-node (n i)
+  (let* ((tt (js2-node-type n))
+         (op (gethash tt js2-operator-tokens))
+         (postfix (js2-node-get-prop n 'postfix)))
+    (unless op
+      (error "unrecognized unary operator %s" tt))
+    (insert (js2-make-pad i))
+    (unless postfix
+      (insert op))
+    (if (or (= tt js2-TYPEOF)
+            (= tt js2-DELPROP))
+        (insert " "))
+    (js2-print-ast (js2-unary-node-operand n) 0)
+    (when postfix
+      (insert op))))
+
+(cl-defstruct (js2-let-node
+               (:include js2-scope)
+               (:constructor nil)
+               (:constructor make-js2-let-node (&key (type js2-LETEXPR)
+                                                     (pos (js2-current-token-beg))
+                                                     len vars body
+                                                     lp rp)))
+  "AST node for a let expression or a let statement.
+Note that a let declaration such as let x=6, y=7 is a `js2-var-decl-node'."
+  vars   ; a `js2-var-decl-node'
+  body   ; a `js2-node' representing the expression or body block
+  lp
+  rp)
+
+(put 'cl-struct-js2-let-node 'js2-visitor 'js2-visit-let-node)
+(put 'cl-struct-js2-let-node 'js2-printer 'js2-print-let-node)
+
+(defun js2-visit-let-node (n v)
+  (js2-visit-ast (js2-let-node-vars n) v)
+  (js2-visit-ast (js2-let-node-body n) v))
+
+(defun js2-print-let-node (n i)
+  (insert (js2-make-pad i) "let (")
+  (let ((p (point)))
+    (js2-print-ast (js2-let-node-vars n) 0)
+    (delete-region p (+ p 4)))
+  (insert ") ")
+  (js2-print-ast (js2-let-node-body n) i))
+
+(cl-defstruct (js2-keyword-node
+               (:include js2-node)
+               (:constructor nil)
+               (:constructor make-js2-keyword-node (&key type
+                                                         (pos (js2-current-token-beg))
+                                                         (len (- js2-ts-cursor pos)))))
+  "AST node representing a literal keyword such as `null'.
+Used for `null', `this', `true', `false' and `debugger'.
+The node type is set to js2-NULL, js2-THIS, etc.")
+
+(put 'cl-struct-js2-keyword-node 'js2-visitor 'js2-visit-none)
+(put 'cl-struct-js2-keyword-node 'js2-printer 'js2-print-keyword-node)
+
+(defun js2-print-keyword-node (n i)
+  (insert (js2-make-pad i)
+          (let ((tt (js2-node-type n)))
+            (cond
+             ((= tt js2-THIS) "this")
+             ((= tt js2-SUPER) "super")
+             ((= tt js2-NULL) "null")
+             ((= tt js2-TRUE) "true")
+             ((= tt js2-FALSE) "false")
+             ((= tt js2-DEBUGGER) "debugger")
+             (t (error "Invalid keyword literal type: %d" tt))))))
+
+(defsubst js2-this-or-super-node-p (node)
+  "Return t if NODE is a `js2-literal-node' of type js2-THIS or js2-SUPER."
+  (let ((type (js2-node-type node)))
+    (or (eq type js2-THIS) (eq type js2-SUPER))))
+
+(cl-defstruct (js2-new-node
+               (:include js2-node)
+               (:constructor nil)
+               (:constructor make-js2-new-node (&key (type js2-NEW)
+                                                     (pos (js2-current-token-beg))
+                                                     len target
+                                                     args initializer
+                                                     lp rp)))
+  "AST node for new-expression such as new Foo()."
+  target  ; an identifier or reference
+  args    ; a Lisp list of argument nodes
+  lp      ; position of left-paren, nil if omitted
+  rp      ; position of right-paren, nil if omitted
+  initializer) ; experimental Rhino syntax:  optional `js2-object-node'
+
+(put 'cl-struct-js2-new-node 'js2-visitor 'js2-visit-new-node)
+(put 'cl-struct-js2-new-node 'js2-printer 'js2-print-new-node)
+
+(defun js2-visit-new-node (n v)
+  (js2-visit-ast (js2-new-node-target n) v)
+  (dolist (arg (js2-new-node-args n))
+    (js2-visit-ast arg v))
+  (js2-visit-ast (js2-new-node-initializer n) v))
+
+(defun js2-print-new-node (n i)
+  (insert (js2-make-pad i) "new ")
+  (js2-print-ast (js2-new-node-target n))
+  (insert "(")
+  (js2-print-list (js2-new-node-args n))
+  (insert ")")
+  (when (js2-new-node-initializer n)
+    (insert " ")
+    (js2-print-ast (js2-new-node-initializer n))))
+
+(cl-defstruct (js2-name-node
+               (:include js2-node)
+               (:constructor nil)
+               (:constructor make-js2-name-node (&key (type js2-NAME)
+                                                      (pos (js2-current-token-beg))
+                                                      (len (- js2-ts-cursor
+                                                              (js2-current-token-beg)))
+                                                      (name (js2-current-token-string)))))
+  "AST node for a JavaScript identifier"
+  name   ; a string
+  scope) ; a `js2-scope' (optional, used for codegen)
+
+(put 'cl-struct-js2-name-node 'js2-visitor 'js2-visit-none)
+(put 'cl-struct-js2-name-node 'js2-printer 'js2-print-name-node)
+
+(defun js2-print-name-node (n i)
+  (insert (js2-make-pad i)
+          (js2-name-node-name n)))
+
+(defsubst js2-name-node-length (node)
+  "Return identifier length of NODE, a `js2-name-node'.
+Returns 0 if NODE is nil or its identifier field is nil."
+  (if node
+      (length (js2-name-node-name node))
+    0))
+
+(cl-defstruct (js2-number-node
+               (:include js2-node)
+               (:constructor nil)
+               (:constructor make-js2-number-node (&key (type js2-NUMBER)
+                                                        (pos (js2-current-token-beg))
+                                                        (len (- js2-ts-cursor
+                                                                (js2-current-token-beg)))
+                                                        (value (js2-current-token-string))
+                                                        (num-value (js2-token-number
+                                                                    (js2-current-token))))))
+  "AST node for a number literal."
+  value      ; the original string, e.g. "6.02e23"
+  num-value) ; the parsed number value
+
+(put 'cl-struct-js2-number-node 'js2-visitor 'js2-visit-none)
+(put 'cl-struct-js2-number-node 'js2-printer 'js2-print-number-node)
+
+(defun js2-print-number-node (n i)
+  (insert (js2-make-pad i)
+          (number-to-string (js2-number-node-num-value n))))
+
+(cl-defstruct (js2-regexp-node
+               (:include js2-node)
+               (:constructor nil)
+               (:constructor make-js2-regexp-node (&key (type js2-REGEXP)
+                                                        (pos (js2-current-token-beg))
+                                                        (len (- js2-ts-cursor
+                                                                (js2-current-token-beg)))
+                                                        value flags)))
+  "AST node for a regular expression literal."
+  value  ; the regexp string, without // delimiters
+  flags) ; a string of flags, e.g. `mi'.
+
+(put 'cl-struct-js2-regexp-node 'js2-visitor 'js2-visit-none)
+(put 'cl-struct-js2-regexp-node 'js2-printer 'js2-print-regexp)
+
+(defun js2-print-regexp (n i)
+  (insert (js2-make-pad i)
+          "/"
+          (js2-regexp-node-value n)
+          "/")
+  (if (js2-regexp-node-flags n)
+      (insert (js2-regexp-node-flags n))))
+
+(cl-defstruct (js2-string-node
+               (:include js2-node)
+               (:constructor nil)
+               (:constructor make-js2-string-node (&key (type js2-STRING)
+                                                        (pos (js2-current-token-beg))
+                                                        (len (- js2-ts-cursor
+                                                                (js2-current-token-beg)))
+                                                        (value (js2-current-token-string)))))
+  "String literal.
+Escape characters are not evaluated; e.g. \n is 2 chars in value field.
+You can tell the quote type by looking at the first character."
+  value) ; the characters of the string, including the quotes
+
+(put 'cl-struct-js2-string-node 'js2-visitor 'js2-visit-none)
+(put 'cl-struct-js2-string-node 'js2-printer 'js2-print-string-node)
+
+(defun js2-print-string-node (n i)
+  (insert (js2-make-pad i)
+          (js2-node-string n)))
+
+(cl-defstruct (js2-template-node
+               (:include js2-node)
+               (:constructor nil)
+               (:constructor make-js2-template-node (&key (type js2-TEMPLATE_HEAD)
+                                                          beg len kids)))
+  "Template literal."
+  kids)  ; `js2-string-node' is used for string segments, other nodes
+         ; for substitutions inside.
+
+(put 'cl-struct-js2-template-node 'js2-visitor 'js2-visit-template)
+(put 'cl-struct-js2-template-node 'js2-printer 'js2-print-template)
+
+(defun js2-visit-template (n callback)
+  (dolist (kid (js2-template-node-kids n))
+    (js2-visit-ast kid callback)))
+
+(defun js2-print-template (n i)
+  (insert (js2-make-pad i))
+  (dolist (kid (js2-template-node-kids n))
+    (if (js2-string-node-p kid)
+        (insert (js2-node-string kid))
+      (js2-print-ast kid))))
+
+(cl-defstruct (js2-tagged-template-node
+               (:include js2-node)
+               (:constructor nil)
+               (:constructor make-js2-tagged-template-node (&key (type js2-TAGGED_TEMPLATE)
+                                                                 beg len tag template)))
+  "Tagged template literal."
+  tag       ; `js2-node' with the tag expression.
+  template) ; `js2-template-node' with the template.
+
+(put 'cl-struct-js2-tagged-template-node 'js2-visitor 'js2-visit-tagged-template)
+(put 'cl-struct-js2-tagged-template-node 'js2-printer 'js2-print-tagged-template)
+
+(defun js2-visit-tagged-template (n callback)
+  (js2-visit-ast (js2-tagged-template-node-tag n) callback)
+  (js2-visit-ast (js2-tagged-template-node-template n) callback))
+
+(defun js2-print-tagged-template (n i)
+  (insert (js2-make-pad i))
+  (js2-print-ast (js2-tagged-template-node-tag n))
+  (js2-print-ast (js2-tagged-template-node-template n)))
+
+(cl-defstruct (js2-array-node
+               (:include js2-node)
+               (:constructor nil)
+               (:constructor make-js2-array-node (&key (type js2-ARRAYLIT)
+                                                       (pos js2-ts-cursor)
+                                                       len elems)))
+  "AST node for an array literal."
+  elems)  ; list of expressions.  [foo,,bar] yields a nil middle element.
+
+(put 'cl-struct-js2-array-node 'js2-visitor 'js2-visit-array-node)
+(put 'cl-struct-js2-array-node 'js2-printer 'js2-print-array-node)
+
+(defun js2-visit-array-node (n v)
+  (dolist (e (js2-array-node-elems n))
+    (js2-visit-ast e v)))  ; Can be nil; e.g. [a, ,b].
+
+(defun js2-print-array-node (n i)
+  (insert (js2-make-pad i) "[")
+  (let ((elems (js2-array-node-elems n)))
+    (js2-print-list elems)
+    (when (and elems (null (car (last elems))))
+      (insert ",")))
+  (insert "]"))
+
+(cl-defstruct (js2-class-node
+               (:include js2-node)
+               (:constructor nil)
+               (:constructor make-js2-class-node (&key (type js2-CLASS)
+                                                       (pos js2-ts-cursor)
+                                                       (form 'CLASS_STATEMENT)
+                                                       (name "")
+                                                       extends len elems)))
+  "AST node for an class expression.
+`elems' is a list of `js2-object-prop-node', and `extends' is an
+optional `js2-expr-node'"
+  form             ; CLASS_{STATEMENT|EXPRESSION}
+  name             ; class name (a `js2-node-name', or nil if anonymous)
+  extends          ; class heritage (a `js2-expr-node', or nil if none)
+  elems)
+
+(put 'cl-struct-js2-class-node 'js2-visitor 'js2-visit-class-node)
+(put 'cl-struct-js2-class-node 'js2-printer 'js2-print-class-node)
+
+(defun js2-visit-class-node (n v)
+  (js2-visit-ast (js2-class-node-name n) v)
+  (js2-visit-ast (js2-class-node-extends n) v)
+  (dolist (e (js2-class-node-elems n))
+    (js2-visit-ast e v)))
+
+(defun js2-print-class-node (n i)
+  (let* ((pad (js2-make-pad i))
+         (name (js2-class-node-name n))
+         (extends (js2-class-node-extends n))
+         (elems (js2-class-node-elems n)))
+    (insert pad "class")
+    (when name
+      (insert " ")
+      (js2-print-ast name 0))
+    (when extends
+      (insert " extends ")
+      (js2-print-ast extends))
+    (insert " {")
+    (dolist (elem elems)
+      (insert "\n")
+      (if (js2-node-get-prop elem 'STATIC)
+          (progn (insert (js2-make-pad (1+ i)) "static ")
+                 (js2-print-ast elem 0)) ;; TODO(sdh): indentation isn't quite right
+        (js2-print-ast elem (1+ i))))
+    (insert "\n" pad "}")))
+
+(cl-defstruct (js2-object-node
+               (:include js2-node)
+               (:constructor nil)
+               (:constructor make-js2-object-node (&key (type js2-OBJECTLIT)
+                                                        (pos js2-ts-cursor)
+                                                        len
+                                                        elems)))
+  "AST node for an object literal expression.
+`elems' is a list of `js2-object-prop-node'."
+  elems)
+
+(put 'cl-struct-js2-object-node 'js2-visitor 'js2-visit-object-node)
+(put 'cl-struct-js2-object-node 'js2-printer 'js2-print-object-node)
+
+(defun js2-visit-object-node (n v)
+  (dolist (e (js2-object-node-elems n))
+    (js2-visit-ast e v)))
+
+(defun js2-print-object-node (n i)
+  (insert (js2-make-pad i) "{")
+  (js2-print-list (js2-object-node-elems n))
+  (insert "}"))
+
+(cl-defstruct (js2-object-prop-node
+               (:include js2-infix-node)
+               (:constructor nil)
+               (:constructor make-js2-object-prop-node (&key (type js2-COLON)
+                                                             (pos js2-ts-cursor)
+                                                             len left
+                                                             right op-pos)))
+  "AST node for an object literal prop:value entry.
+The `left' field is the property:  a name node, string node or number node.
+The `right' field is a `js2-node' representing the initializer value.
+If the property is abbreviated, the node's `SHORTHAND' property is non-nil
+and both fields have the same value.")
+
+(put 'cl-struct-js2-object-prop-node 'js2-visitor 'js2-visit-infix-node)
+(put 'cl-struct-js2-object-prop-node 'js2-printer 'js2-print-object-prop-node)
+
+(defun js2-print-object-prop-node (n i)
+  (let* ((left (js2-object-prop-node-left n))
+         (computed (not (or (js2-string-node-p left)
+                            (js2-number-node-p left)
+                            (js2-name-node-p left)))))
+    (insert (js2-make-pad i))
+    (if computed
+        (insert "["))
+    (js2-print-ast left 0)
+    (if computed
+        (insert "]"))
+    (if (not (js2-node-get-prop n 'SHORTHAND))
+        (progn
+          (insert ": ")
+          (js2-print-ast (js2-object-prop-node-right n) 0)))))
+
+(cl-defstruct (js2-getter-setter-node
+               (:include js2-infix-node)
+               (:constructor nil)
+               (:constructor make-js2-getter-setter-node (&key type ; GET, SET, or FUNCTION
+                                                               (pos js2-ts-cursor)
+                                                               len left right)))
+  "AST node for a getter/setter property in an object literal.
+The `left' field is the `js2-name-node' naming the getter/setter prop.
+The `right' field is always an anonymous `js2-function-node' with a node
+property `GETTER_SETTER' set to js2-GET, js2-SET, or js2-FUNCTION. ")
+
+(put 'cl-struct-js2-getter-setter-node 'js2-visitor 'js2-visit-infix-node)
+(put 'cl-struct-js2-getter-setter-node 'js2-printer 'js2-print-getter-setter)
+
+(defun js2-print-getter-setter (n i)
+  (let ((pad (js2-make-pad i))
+        (left (js2-getter-setter-node-left n))
+        (right (js2-getter-setter-node-right n)))
+    (insert pad)
+    (if (/= (js2-node-type n) js2-FUNCTION)
+        (insert (if (= (js2-node-type n) js2-GET) "get " "set ")))
+    (js2-print-ast left 0)
+    (js2-print-ast right 0)))
+
+(cl-defstruct (js2-prop-get-node
+               (:include js2-infix-node)
+               (:constructor nil)
+               (:constructor make-js2-prop-get-node (&key (type js2-GETPROP)
+                                                          (pos js2-ts-cursor)
+                                                          len left right)))
+  "AST node for a dotted property reference, e.g. foo.bar or foo().bar")
+
+(put 'cl-struct-js2-prop-get-node 'js2-visitor 'js2-visit-prop-get-node)
+(put 'cl-struct-js2-prop-get-node 'js2-printer 'js2-print-prop-get-node)
+
+(defun js2-visit-prop-get-node (n v)
+  (js2-visit-ast (js2-prop-get-node-left n) v)
+  (js2-visit-ast (js2-prop-get-node-right n) v))
+
+(defun js2-print-prop-get-node (n i)
+  (insert (js2-make-pad i))
+  (js2-print-ast (js2-prop-get-node-left n) 0)
+  (insert ".")
+  (js2-print-ast (js2-prop-get-node-right n) 0))
+
+(cl-defstruct (js2-elem-get-node
+               (:include js2-node)
+               (:constructor nil)
+               (:constructor make-js2-elem-get-node (&key (type js2-GETELEM)
+                                                          (pos js2-ts-cursor)
+                                                          len target element
+                                                          lb rb)))
+  "AST node for an array index expression such as foo[bar]."
+  target  ; a `js2-node' - the expression preceding the "."
+  element ; a `js2-node' - the expression in brackets
+  lb      ; position of left-bracket, nil if omitted
+  rb)     ; position of right-bracket, nil if omitted
+
+(put 'cl-struct-js2-elem-get-node 'js2-visitor 'js2-visit-elem-get-node)
+(put 'cl-struct-js2-elem-get-node 'js2-printer 'js2-print-elem-get-node)
+
+(defun js2-visit-elem-get-node (n v)
+  (js2-visit-ast (js2-elem-get-node-target n) v)
+  (js2-visit-ast (js2-elem-get-node-element n) v))
+
+(defun js2-print-elem-get-node (n i)
+  (insert (js2-make-pad i))
+  (js2-print-ast (js2-elem-get-node-target n) 0)
+  (insert "[")
+  (js2-print-ast (js2-elem-get-node-element n) 0)
+  (insert "]"))
+
+(cl-defstruct (js2-call-node
+               (:include js2-node)
+               (:constructor nil)
+               (:constructor make-js2-call-node (&key (type js2-CALL)
+                                                      (pos js2-ts-cursor)
+                                                      len target args
+                                                      lp rp)))
+  "AST node for a JavaScript function call."
+  target  ; a `js2-node' evaluating to the function to call
+  args  ; a Lisp list of `js2-node' arguments
+  lp    ; position of open-paren, or nil if missing
+  rp)   ; position of close-paren, or nil if missing
+
+(put 'cl-struct-js2-call-node 'js2-visitor 'js2-visit-call-node)
+(put 'cl-struct-js2-call-node 'js2-printer 'js2-print-call-node)
+
+(defun js2-visit-call-node (n v)
+  (js2-visit-ast (js2-call-node-target n) v)
+  (dolist (arg (js2-call-node-args n))
+    (js2-visit-ast arg v)))
+
+(defun js2-print-call-node (n i)
+  (insert (js2-make-pad i))
+  (js2-print-ast (js2-call-node-target n) 0)
+  (insert "(")
+  (js2-print-list (js2-call-node-args n))
+  (insert ")"))
+
+(cl-defstruct (js2-yield-node
+               (:include js2-node)
+               (:constructor nil)
+               (:constructor make-js2-yield-node (&key (type js2-YIELD)
+                                                       (pos js2-ts-cursor)
+                                                       len value star-p)))
+  "AST node for yield statement or expression."
+  star-p ; whether it's yield*
+  value) ; optional:  value to be yielded
+
+(put 'cl-struct-js2-yield-node 'js2-visitor 'js2-visit-yield-node)
+(put 'cl-struct-js2-yield-node 'js2-printer 'js2-print-yield-node)
+
+(defun js2-visit-yield-node (n v)
+  (js2-visit-ast (js2-yield-node-value n) v))
+
+(defun js2-print-yield-node (n i)
+  (insert (js2-make-pad i))
+  (insert "yield")
+  (when (js2-yield-node-star-p n)
+    (insert "*"))
+  (when (js2-yield-node-value n)
+    (insert " ")
+    (js2-print-ast (js2-yield-node-value n) 0)))
+
+(cl-defstruct (js2-paren-node
+               (:include js2-node)
+               (:constructor nil)
+               (:constructor make-js2-paren-node (&key (type js2-LP)
+                                                       (pos js2-ts-cursor)
+                                                       len expr)))
+  "AST node for a parenthesized expression.
+In particular, used when the parens are syntactically optional,
+as opposed to required parens such as those enclosing an if-conditional."
+  expr)   ; `js2-node'
+
+(put 'cl-struct-js2-paren-node 'js2-visitor 'js2-visit-paren-node)
+(put 'cl-struct-js2-paren-node 'js2-printer 'js2-print-paren-node)
+
+(defun js2-visit-paren-node (n v)
+  (js2-visit-ast (js2-paren-node-expr n) v))
+
+(defun js2-print-paren-node (n i)
+  (insert (js2-make-pad i))
+  (insert "(")
+  (js2-print-ast (js2-paren-node-expr n) 0)
+  (insert ")"))
+
+(cl-defstruct (js2-comp-node
+               (:include js2-scope)
+               (:constructor nil)
+               (:constructor make-js2-comp-node (&key (type js2-ARRAYCOMP)
+                                                      (pos js2-ts-cursor)
+                                                      len result
+                                                      loops filters
+                                                      form)))
+  "AST node for an Array comprehension such as [[x,y] for (x in foo) for (y in bar)]."
+  result  ; result expression (just after left-bracket)
+  loops   ; a Lisp list of `js2-comp-loop-node'
+  filters ; a Lisp list of guard/filter expressions
+  form    ; ARRAY, LEGACY_ARRAY or STAR_GENERATOR
+          ; SpiderMonkey also supports "legacy generator expressions", but we dont.
+  )
+
+(put 'cl-struct-js2-comp-node 'js2-visitor 'js2-visit-comp-node)
+(put 'cl-struct-js2-comp-node 'js2-printer 'js2-print-comp-node)
+
+(defun js2-visit-comp-node (n v)
+  (js2-visit-ast (js2-comp-node-result n) v)
+  (dolist (l (js2-comp-node-loops n))
+    (js2-visit-ast l v))
+  (dolist (f (js2-comp-node-filters n))
+    (js2-visit-ast f v)))
+
+(defun js2-print-comp-node (n i)
+  (let ((pad (js2-make-pad i))
+        (result (js2-comp-node-result n))
+        (loops (js2-comp-node-loops n))
+        (filters (js2-comp-node-filters n))
+        (legacy-p (eq (js2-comp-node-form n) 'LEGACY_ARRAY))
+        (gen-p (eq (js2-comp-node-form n) 'STAR_GENERATOR)))
+    (insert pad (if gen-p "(" "["))
+    (when legacy-p
+      (js2-print-ast result 0))
+    (dolist (l loops)
+      (when legacy-p
+        (insert " "))
+      (js2-print-ast l 0)
+      (unless legacy-p
+        (insert " ")))
+    (dolist (f filters)
+      (when legacy-p
+        (insert " "))
+      (insert "if (")
+      (js2-print-ast f 0)
+      (insert ")")
+      (unless legacy-p
+        (insert " ")))
+    (unless legacy-p
+      (js2-print-ast result 0))
+    (insert (if gen-p ")" "]"))))
+
+(cl-defstruct (js2-comp-loop-node
+               (:include js2-for-in-node)
+               (:constructor nil)
+               (:constructor make-js2-comp-loop-node (&key (type js2-FOR)
+                                                           (pos js2-ts-cursor)
+                                                           len iterator
+                                                           object in-pos
+                                                           foreach-p
+                                                           each-pos
+                                                           forof-p
+                                                           lp rp)))
+  "AST subtree for each 'for (foo in bar)' loop in an array comprehension.")
+
+(put 'cl-struct-js2-comp-loop-node 'js2-visitor 'js2-visit-comp-loop)
+(put 'cl-struct-js2-comp-loop-node 'js2-printer 'js2-print-comp-loop)
+
+(defun js2-visit-comp-loop (n v)
+  (js2-visit-ast (js2-comp-loop-node-iterator n) v)
+  (js2-visit-ast (js2-comp-loop-node-object n) v))
+
+(defun js2-print-comp-loop (n _i)
+  (insert "for ")
+  (when (js2-comp-loop-node-foreach-p n) (insert "each "))
+  (insert "(")
+  (js2-print-ast (js2-comp-loop-node-iterator n) 0)
+  (insert (if (js2-comp-loop-node-forof-p n)
+              " of " " in "))
+  (js2-print-ast (js2-comp-loop-node-object n) 0)
+  (insert ")"))
+
+(cl-defstruct (js2-empty-expr-node
+               (:include js2-node)
+               (:constructor nil)
+               (:constructor make-js2-empty-expr-node (&key (type js2-EMPTY)
+                                                            (pos (js2-current-token-beg))
+                                                            len)))
+  "AST node for an empty expression.")
+
+(put 'cl-struct-js2-empty-expr-node 'js2-visitor 'js2-visit-none)
+(put 'cl-struct-js2-empty-expr-node 'js2-printer 'js2-print-none)
+
+(cl-defstruct (js2-xml-node
+               (:include js2-block-node)
+               (:constructor nil)
+               (:constructor make-js2-xml-node (&key (type js2-XML)
+                                                     (pos (js2-current-token-beg))
+                                                     len kids)))
+  "AST node for initial parse of E4X literals.
+The kids field is a list of XML fragments, each a `js2-string-node' or
+a `js2-xml-js-expr-node'.  Equivalent to Rhino's XmlLiteral node.")
+
+(put 'cl-struct-js2-xml-node 'js2-visitor 'js2-visit-block)
+(put 'cl-struct-js2-xml-node 'js2-printer 'js2-print-xml-node)
+
+(defun js2-print-xml-node (n i)
+  (dolist (kid (js2-xml-node-kids n))
+    (js2-print-ast kid i)))
+
+(cl-defstruct (js2-xml-js-expr-node
+               (:include js2-xml-node)
+               (:constructor nil)
+               (:constructor make-js2-xml-js-expr-node (&key (type js2-XML)
+                                                             (pos js2-ts-cursor)
+                                                             len expr)))
+  "AST node for an embedded JavaScript {expression} in an E4X literal.
+The start and end fields correspond to the curly-braces."
+  expr)  ; a `js2-expr-node' of some sort
+
+(put 'cl-struct-js2-xml-js-expr-node 'js2-visitor 'js2-visit-xml-js-expr)
+(put 'cl-struct-js2-xml-js-expr-node 'js2-printer 'js2-print-xml-js-expr)
+
+(defun js2-visit-xml-js-expr (n v)
+  (js2-visit-ast (js2-xml-js-expr-node-expr n) v))
+
+(defun js2-print-xml-js-expr (n i)
+  (insert (js2-make-pad i))
+  (insert "{")
+  (js2-print-ast (js2-xml-js-expr-node-expr n) 0)
+  (insert "}"))
+
+(cl-defstruct (js2-xml-dot-query-node
+               (:include js2-infix-node)
+               (:constructor nil)
+               (:constructor make-js2-xml-dot-query-node (&key (type js2-DOTQUERY)
+                                                               (pos js2-ts-cursor)
+                                                               op-pos len left
+                                                               right rp)))
+  "AST node for an E4X foo.(bar) filter expression.
+Note that the left-paren is automatically the character immediately
+following the dot (.) in the operator.  No whitespace is permitted
+between the dot and the lp by the scanner."
+  rp)
+
+(put 'cl-struct-js2-xml-dot-query-node 'js2-visitor 'js2-visit-infix-node)
+(put 'cl-struct-js2-xml-dot-query-node 'js2-printer 'js2-print-xml-dot-query)
+
+(defun js2-print-xml-dot-query (n i)
+  (insert (js2-make-pad i))
+  (js2-print-ast (js2-xml-dot-query-node-left n) 0)
+  (insert ".(")
+  (js2-print-ast (js2-xml-dot-query-node-right n) 0)
+  (insert ")"))
+
+(cl-defstruct (js2-xml-ref-node
+               (:include js2-node)
+               (:constructor nil))  ; abstract
+  "Base type for E4X XML attribute-access or property-get expressions.
+Such expressions can take a variety of forms.  The general syntax has
+three parts:
+
+  - (optional) an @ (specifying an attribute access)
+  - (optional) a namespace (a `js2-name-node') and double-colon
+  - (required) either a `js2-name-node' or a bracketed [expression]
+
+The property-name expressions (examples:  ns::name, @name) are
+represented as `js2-xml-prop-ref' nodes.  The bracketed-expression
+versions (examples:  ns::[name], @[name]) become `js2-xml-elem-ref' nodes.
+
+This node type (or more specifically, its subclasses) will sometimes
+be the right-hand child of a `js2-prop-get-node' or a
+`js2-infix-node' of type `js2-DOTDOT', the .. xml-descendants operator.
+The `js2-xml-ref-node' may also be a standalone primary expression with
+no explicit target, which is valid in certain expression contexts such as
+
+  company..employee.(@id < 100)
+
+in this case, the @id is a `js2-xml-ref' that is part of an infix '<'
+expression whose parent is a `js2-xml-dot-query-node'."
+  namespace
+  at-pos
+  colon-pos)
+
+(defsubst js2-xml-ref-node-attr-access-p (node)
+  "Return non-nil if this expression began with an @-token."
+  (and (numberp (js2-xml-ref-node-at-pos node))
+       (cl-plusp (js2-xml-ref-node-at-pos node))))
+
+(cl-defstruct (js2-xml-prop-ref-node
+               (:include js2-xml-ref-node)
+               (:constructor nil)
+               (:constructor make-js2-xml-prop-ref-node (&key (type js2-REF_NAME)
+                                                              (pos (js2-current-token-beg))
+                                                              len propname
+                                                              namespace at-pos
+                                                              colon-pos)))
+  "AST node for an E4X XML [expr] property-ref expression.
+The JavaScript syntax is an optional @, an optional ns::, and a name.
+
+  [ '@' ] [ name '::' ] name
+
+Examples include name, ns::name, ns::*, *::name, *::*, @attr, @ns::attr,
+@ns::*, @*::attr, @*::*, and @*.
+
+The node starts at the @ token, if present.  Otherwise it starts at the
+namespace name.  The node bounds extend through the closing right-bracket,
+or if it is missing due to a syntax error, through the end of the index
+expression."
+  propname)
+
+(put 'cl-struct-js2-xml-prop-ref-node 'js2-visitor 'js2-visit-xml-prop-ref-node)
+(put 'cl-struct-js2-xml-prop-ref-node 'js2-printer 'js2-print-xml-prop-ref-node)
+
+(defun js2-visit-xml-prop-ref-node (n v)
+  (js2-visit-ast (js2-xml-prop-ref-node-namespace n) v)
+  (js2-visit-ast (js2-xml-prop-ref-node-propname n) v))
+
+(defun js2-print-xml-prop-ref-node (n i)
+  (insert (js2-make-pad i))
+  (if (js2-xml-ref-node-attr-access-p n)
+      (insert "@"))
+  (when (js2-xml-prop-ref-node-namespace n)
+    (js2-print-ast (js2-xml-prop-ref-node-namespace n) 0)
+    (insert "::"))
+  (if (js2-xml-prop-ref-node-propname n)
+      (js2-print-ast (js2-xml-prop-ref-node-propname n) 0)))
+
+(cl-defstruct (js2-xml-elem-ref-node
+               (:include js2-xml-ref-node)
+               (:constructor nil)
+               (:constructor make-js2-xml-elem-ref-node (&key (type js2-REF_MEMBER)
+                                                              (pos (js2-current-token-beg))
+                                                              len expr lb rb
+                                                              namespace at-pos
+                                                              colon-pos)))
+  "AST node for an E4X XML [expr] member-ref expression.
+Syntax:
+
+ [ '@' ] [ name '::' ] '[' expr ']'
+
+Examples include ns::[expr], @ns::[expr], @[expr], *::[expr] and @*::[expr].
+
+Note that the form [expr] (i.e. no namespace or attribute-qualifier)
+is not a legal E4X XML element-ref expression, since it's already used
+for standard JavaScript element-get array indexing.  Hence, a
+`js2-xml-elem-ref-node' always has either the attribute-qualifier, a
+non-nil namespace node, or both.
+
+The node starts at the @ token, if present.  Otherwise it starts
+at the namespace name.  The node bounds extend through the closing
+right-bracket, or if it is missing due to a syntax error, through the
+end of the index expression."
+  expr  ; the bracketed index expression
+  lb
+  rb)
+
+(put 'cl-struct-js2-xml-elem-ref-node 'js2-visitor 'js2-visit-xml-elem-ref-node)
+(put 'cl-struct-js2-xml-elem-ref-node 'js2-printer 'js2-print-xml-elem-ref-node)
+
+(defun js2-visit-xml-elem-ref-node (n v)
+  (js2-visit-ast (js2-xml-elem-ref-node-namespace n) v)
+  (js2-visit-ast (js2-xml-elem-ref-node-expr n) v))
+
+(defun js2-print-xml-elem-ref-node (n i)
+  (insert (js2-make-pad i))
+  (if (js2-xml-ref-node-attr-access-p n)
+      (insert "@"))
+  (when (js2-xml-elem-ref-node-namespace n)
+    (js2-print-ast (js2-xml-elem-ref-node-namespace n) 0)
+    (insert "::"))
+  (insert "[")
+  (if (js2-xml-elem-ref-node-expr n)
+      (js2-print-ast (js2-xml-elem-ref-node-expr n) 0))
+  (insert "]"))
+
+;;; Placeholder nodes for when we try parsing the XML literals structurally.
+
+(cl-defstruct (js2-xml-start-tag-node
+               (:include js2-xml-node)
+               (:constructor nil)
+               (:constructor make-js2-xml-start-tag-node (&key (type js2-XML)
+                                                               (pos js2-ts-cursor)
+                                                               len name attrs kids
+                                                               empty-p)))
+  "AST node for an XML start-tag.  Not currently used.
+The `kids' field is a Lisp list of child content nodes."
+  name      ; a `js2-xml-name-node'
+  attrs     ; a Lisp list of `js2-xml-attr-node'
+  empty-p)  ; t if this is an empty element such as <foo bar="baz"/>
+
+(put 'cl-struct-js2-xml-start-tag-node 'js2-visitor 'js2-visit-xml-start-tag)
+(put 'cl-struct-js2-xml-start-tag-node 'js2-printer 'js2-print-xml-start-tag)
+
+(defun js2-visit-xml-start-tag (n v)
+  (js2-visit-ast (js2-xml-start-tag-node-name n) v)
+  (dolist (attr (js2-xml-start-tag-node-attrs n))
+    (js2-visit-ast attr v))
+  (js2-visit-block n v))
+
+(defun js2-print-xml-start-tag (n i)
+  (insert (js2-make-pad i) "<")
+  (js2-print-ast (js2-xml-start-tag-node-name n) 0)
+  (when (js2-xml-start-tag-node-attrs n)
+    (insert " ")
+    (js2-print-list (js2-xml-start-tag-node-attrs n) " "))
+  (insert ">"))
+
+;; I -think- I'm going to make the parent node the corresponding start-tag,
+;; and add the end-tag to the kids list of the parent as well.
+(cl-defstruct (js2-xml-end-tag-node
+               (:include js2-xml-node)
+               (:constructor nil)
+               (:constructor make-js2-xml-end-tag-node (&key (type js2-XML)
+                                                             (pos js2-ts-cursor)
+                                                             len name)))
+  "AST node for an XML end-tag.  Not currently used."
+  name)  ; a `js2-xml-name-node'
+
+(put 'cl-struct-js2-xml-end-tag-node 'js2-visitor 'js2-visit-xml-end-tag)
+(put 'cl-struct-js2-xml-end-tag-node 'js2-printer 'js2-print-xml-end-tag)
+
+(defun js2-visit-xml-end-tag (n v)
+  (js2-visit-ast (js2-xml-end-tag-node-name n) v))
+
+(defun js2-print-xml-end-tag (n i)
+  (insert (js2-make-pad i))
+  (insert "</")
+  (js2-print-ast (js2-xml-end-tag-node-name n) 0)
+  (insert ">"))
+
+(cl-defstruct (js2-xml-name-node
+               (:include js2-xml-node)
+               (:constructor nil)
+               (:constructor make-js2-xml-name-node (&key (type js2-XML)
+                                                          (pos js2-ts-cursor)
+                                                          len namespace kids)))
+  "AST node for an E4X XML name.  Not currently used.
+Any XML name can be qualified with a namespace, hence the namespace field.
+Further, any E4X name can be comprised of arbitrary JavaScript {} expressions.
+The kids field is a list of `js2-name-node' and `js2-xml-js-expr-node'.
+For a simple name, the kids list has exactly one node, a `js2-name-node'."
+  namespace)  ; a `js2-string-node'
+
+(put 'cl-struct-js2-xml-name-node 'js2-visitor 'js2-visit-xml-name-node)
+(put 'cl-struct-js2-xml-name-node 'js2-printer 'js2-print-xml-name-node)
+
+(defun js2-visit-xml-name-node (n v)
+  (js2-visit-ast (js2-xml-name-node-namespace n) v))
+
+(defun js2-print-xml-name-node (n i)
+  (insert (js2-make-pad i))
+  (when (js2-xml-name-node-namespace n)
+    (js2-print-ast (js2-xml-name-node-namespace n) 0)
+    (insert "::"))
+  (dolist (kid (js2-xml-name-node-kids n))
+    (js2-print-ast kid 0)))
+
+(cl-defstruct (js2-xml-pi-node
+               (:include js2-xml-node)
+               (:constructor nil)
+               (:constructor make-js2-xml-pi-node (&key (type js2-XML)
+                                                        (pos js2-ts-cursor)
+                                                        len name attrs)))
+  "AST node for an E4X XML processing instruction.  Not currently used."
+  name   ; a `js2-xml-name-node'
+  attrs) ; a list of `js2-xml-attr-node'
+
+(put 'cl-struct-js2-xml-pi-node 'js2-visitor 'js2-visit-xml-pi-node)
+(put 'cl-struct-js2-xml-pi-node 'js2-printer 'js2-print-xml-pi-node)
+
+(defun js2-visit-xml-pi-node (n v)
+  (js2-visit-ast (js2-xml-pi-node-name n) v)
+  (dolist (attr (js2-xml-pi-node-attrs n))
+    (js2-visit-ast attr v)))
+
+(defun js2-print-xml-pi-node (n i)
+  (insert (js2-make-pad i) "<?")
+  (js2-print-ast (js2-xml-pi-node-name n))
+  (when (js2-xml-pi-node-attrs n)
+    (insert " ")
+    (js2-print-list (js2-xml-pi-node-attrs n)))
+  (insert "?>"))
+
+(cl-defstruct (js2-xml-cdata-node
+               (:include js2-xml-node)
+               (:constructor nil)
+               (:constructor make-js2-xml-cdata-node (&key (type js2-XML)
+                                                           (pos js2-ts-cursor)
+                                                           len content)))
+  "AST node for a CDATA escape section.  Not currently used."
+  content)  ; a `js2-string-node' with node-property 'quote-type 'cdata
+
+(put 'cl-struct-js2-xml-cdata-node 'js2-visitor 'js2-visit-xml-cdata-node)
+(put 'cl-struct-js2-xml-cdata-node 'js2-printer 'js2-print-xml-cdata-node)
+
+(defun js2-visit-xml-cdata-node (n v)
+  (js2-visit-ast (js2-xml-cdata-node-content n) v))
+
+(defun js2-print-xml-cdata-node (n i)
+  (insert (js2-make-pad i))
+  (js2-print-ast (js2-xml-cdata-node-content n)))
+
+(cl-defstruct (js2-xml-attr-node
+               (:include js2-xml-node)
+               (:constructor nil)
+               (:constructor make-js2-attr-node (&key (type js2-XML)
+                                                      (pos js2-ts-cursor)
+                                                      len name value
+                                                      eq-pos quote-type)))
+  "AST node representing a foo='bar' XML attribute value.  Not yet used."
+  name   ; a `js2-xml-name-node'
+  value  ; a `js2-xml-name-node'
+  eq-pos ; buffer position of "=" sign
+  quote-type) ; 'single or 'double
+
+(put 'cl-struct-js2-xml-attr-node 'js2-visitor 'js2-visit-xml-attr-node)
+(put 'cl-struct-js2-xml-attr-node 'js2-printer 'js2-print-xml-attr-node)
+
+(defun js2-visit-xml-attr-node (n v)
+  (js2-visit-ast (js2-xml-attr-node-name n) v)
+  (js2-visit-ast (js2-xml-attr-node-value n) v))
+
+(defun js2-print-xml-attr-node (n i)
+  (let ((quote (if (eq (js2-xml-attr-node-quote-type n) 'single)
+                   "'"
+                 "\"")))
+    (insert (js2-make-pad i))
+    (js2-print-ast (js2-xml-attr-node-name n) 0)
+    (insert "=" quote)
+    (js2-print-ast (js2-xml-attr-node-value n) 0)
+    (insert quote)))
+
+(cl-defstruct (js2-xml-text-node
+               (:include js2-xml-node)
+               (:constructor nil)
+               (:constructor make-js2-text-node (&key (type js2-XML)
+                                                      (pos js2-ts-cursor)
+                                                      len content)))
+  "AST node for an E4X XML text node.  Not currently used."
+  content)  ; a Lisp list of `js2-string-node' and `js2-xml-js-expr-node'
+
+(put 'cl-struct-js2-xml-text-node 'js2-visitor 'js2-visit-xml-text-node)
+(put 'cl-struct-js2-xml-text-node 'js2-printer 'js2-print-xml-text-node)
+
+(defun js2-visit-xml-text-node (n v)
+  (js2-visit-ast (js2-xml-text-node-content n) v))
+
+(defun js2-print-xml-text-node (n i)
+  (insert (js2-make-pad i))
+  (dolist (kid (js2-xml-text-node-content n))
+    (js2-print-ast kid)))
+
+(cl-defstruct (js2-xml-comment-node
+               (:include js2-xml-node)
+               (:constructor nil)
+               (:constructor make-js2-xml-comment-node (&key (type js2-XML)
+                                                             (pos js2-ts-cursor)
+                                                             len)))
+  "AST node for E4X XML comment.  Not currently used.")
+
+(put 'cl-struct-js2-xml-comment-node 'js2-visitor 'js2-visit-none)
+(put 'cl-struct-js2-xml-comment-node 'js2-printer 'js2-print-xml-comment)
+
+(defun js2-print-xml-comment (n i)
+  (insert (js2-make-pad i)
+          (js2-node-string n)))
+
+;;; Node utilities
+
+(defsubst js2-node-line (n)
+  "Fetch the source line number at the start of node N.
+This is O(n) in the length of the source buffer; use prudently."
+  (1+ (count-lines (point-min) (js2-node-abs-pos n))))
+
+(defsubst js2-block-node-kid (n i)
+  "Return child I of node N, or nil if there aren't that many."
+  (nth i (js2-block-node-kids n)))
+
+(defsubst js2-block-node-first (n)
+  "Return first child of block node N, or nil if there is none."
+  (cl-first (js2-block-node-kids n)))
+
+(defun js2-node-root (n)
+  "Return the root of the AST containing N.
+If N has no parent pointer, returns N."
+  (let ((parent (js2-node-parent n)))
+    (if parent
+        (js2-node-root parent)
+      n)))
+
+(defsubst js2-node-short-name (n)
+  "Return the short name of node N as a string, e.g. `js2-if-node'."
+  (substring (symbol-name (aref n 0))
+             (length "cl-struct-")))
+
+(defun js2-node-child-list (node)
+  "Return the child list for NODE, a Lisp list of nodes.
+Works for block nodes, array nodes, obj literals, funarg lists,
+var decls and try nodes (for catch clauses).  Note that you should call
+`js2-block-node-kids' on the function body for the body statements.
+Returns nil for zero-length child lists or unsupported nodes."
+  (cond
+   ((js2-function-node-p node)
+    (js2-function-node-params node))
+   ((js2-block-node-p node)
+    (js2-block-node-kids node))
+   ((js2-try-node-p node)
+    (js2-try-node-catch-clauses node))
+   ((js2-array-node-p node)
+    (js2-array-node-elems node))
+   ((js2-object-node-p node)
+    (js2-object-node-elems node))
+   ((js2-call-node-p node)
+    (js2-call-node-args node))
+   ((js2-new-node-p node)
+    (js2-new-node-args node))
+   ((js2-var-decl-node-p node)
+    (js2-var-decl-node-kids node))
+   (t
+    nil)))
+
+(defun js2-node-set-child-list (node kids)
+  "Set the child list for NODE to KIDS."
+   (cond
+    ((js2-function-node-p node)
+     (setf (js2-function-node-params node) kids))
+    ((js2-block-node-p node)
+     (setf (js2-block-node-kids node) kids))
+    ((js2-try-node-p node)
+     (setf (js2-try-node-catch-clauses node) kids))
+    ((js2-array-node-p node)
+     (setf (js2-array-node-elems node) kids))
+    ((js2-object-node-p node)
+     (setf (js2-object-node-elems node) kids))
+    ((js2-call-node-p node)
+     (setf (js2-call-node-args node) kids))
+    ((js2-new-node-p node)
+     (setf (js2-new-node-args node) kids))
+    ((js2-var-decl-node-p node)
+     (setf (js2-var-decl-node-kids node) kids))
+    (t
+     (error "Unsupported node type: %s" (js2-node-short-name node))))
+   kids)
+
+;; All because Common Lisp doesn't support multiple inheritance for defstructs.
+(defconst js2-paren-expr-nodes
+  '(cl-struct-js2-comp-loop-node
+    cl-struct-js2-comp-node
+    cl-struct-js2-call-node
+    cl-struct-js2-catch-node
+    cl-struct-js2-do-node
+    cl-struct-js2-elem-get-node
+    cl-struct-js2-for-in-node
+    cl-struct-js2-for-node
+    cl-struct-js2-function-node
+    cl-struct-js2-if-node
+    cl-struct-js2-let-node
+    cl-struct-js2-new-node
+    cl-struct-js2-paren-node
+    cl-struct-js2-switch-node
+    cl-struct-js2-while-node
+    cl-struct-js2-with-node
+    cl-struct-js2-xml-dot-query-node)
+  "Node types that can have a parenthesized child expression.
+In particular, nodes that respond to `js2-node-lp' and `js2-node-rp'.")
+
+(defsubst js2-paren-expr-node-p (node)
+  "Return t for nodes that typically have a parenthesized child expression.
+Useful for computing the indentation anchors for arg-lists and conditions.
+Note that it may return a false positive, for instance when NODE is
+a `js2-new-node' and there are no arguments or parentheses."
+  (memq (aref node 0) js2-paren-expr-nodes))
+
+;; Fake polymorphism... yech.
+(defun js2-node-lp (node)
+  "Return relative left-paren position for NODE, if applicable.
+For `js2-elem-get-node' structs, returns left-bracket position.
+Note that the position may be nil in the case of a parse error."
+  (cond
+   ((js2-elem-get-node-p node)
+    (js2-elem-get-node-lb node))
+   ((js2-loop-node-p node)
+    (js2-loop-node-lp node))
+   ((js2-function-node-p node)
+    (js2-function-node-lp node))
+   ((js2-if-node-p node)
+    (js2-if-node-lp node))
+   ((js2-new-node-p node)
+    (js2-new-node-lp node))
+   ((js2-call-node-p node)
+    (js2-call-node-lp node))
+   ((js2-paren-node-p node)
+    0)
+   ((js2-switch-node-p node)
+    (js2-switch-node-lp node))
+   ((js2-catch-node-p node)
+    (js2-catch-node-lp node))
+   ((js2-let-node-p node)
+    (js2-let-node-lp node))
+   ((js2-comp-node-p node)
+    0)
+   ((js2-with-node-p node)
+    (js2-with-node-lp node))
+   ((js2-xml-dot-query-node-p node)
+    (1+ (js2-infix-node-op-pos node)))
+   (t
+    (error "Unsupported node type: %s" (js2-node-short-name node)))))
+
+;; Fake polymorphism... blech.
+(defun js2-node-rp (node)
+  "Return relative right-paren position for NODE, if applicable.
+For `js2-elem-get-node' structs, returns right-bracket position.
+Note that the position may be nil in the case of a parse error."
+  (cond
+   ((js2-elem-get-node-p node)
+    (js2-elem-get-node-rb node))
+   ((js2-loop-node-p node)
+    (js2-loop-node-rp node))
+   ((js2-function-node-p node)
+    (js2-function-node-rp node))
+   ((js2-if-node-p node)
+    (js2-if-node-rp node))
+   ((js2-new-node-p node)
+    (js2-new-node-rp node))
+   ((js2-call-node-p node)
+    (js2-call-node-rp node))
+   ((js2-paren-node-p node)
+    (1- (js2-node-len node)))
+   ((js2-switch-node-p node)
+    (js2-switch-node-rp node))
+   ((js2-catch-node-p node)
+    (js2-catch-node-rp node))
+   ((js2-let-node-p node)
+    (js2-let-node-rp node))
+   ((js2-comp-node-p node)
+    (1- (js2-node-len node)))
+   ((js2-with-node-p node)
+    (js2-with-node-rp node))
+   ((js2-xml-dot-query-node-p node)
+    (1+ (js2-xml-dot-query-node-rp node)))
+   (t
+    (error "Unsupported node type: %s" (js2-node-short-name node)))))
+
+(defsubst js2-node-first-child (node)
+  "Return the first element of `js2-node-child-list' for NODE."
+  (car (js2-node-child-list node)))
+
+(defsubst js2-node-last-child (node)
+  "Return the last element of `js2-node-last-child' for NODE."
+  (car (last (js2-node-child-list node))))
+
+(defun js2-node-prev-sibling (node)
+  "Return the previous statement in parent.
+Works for parents supported by `js2-node-child-list'.
+Returns nil if NODE is not in the parent, or PARENT is
+not a supported node, or if NODE is the first child."
+  (let* ((p (js2-node-parent node))
+         (kids (js2-node-child-list p))
+         (sib (car kids)))
+    (while (and kids
+                (not (eq node (cadr kids))))
+      (setq kids (cdr kids)
+            sib (car kids)))
+    sib))
+
+(defun js2-node-next-sibling (node)
+  "Return the next statement in parent block.
+Returns nil if NODE is not in the block, or PARENT is not
+a block node, or if NODE is the last statement."
+  (let* ((p (js2-node-parent node))
+         (kids (js2-node-child-list p)))
+    (while (and kids
+                (not (eq node (car kids))))
+      (setq kids (cdr kids)))
+    (cadr kids)))
+
+(defun js2-node-find-child-before (pos parent &optional after)
+  "Find the last child that starts before POS in parent.
+If AFTER is non-nil, returns first child starting after POS.
+POS is an absolute buffer position.  PARENT is any node
+supported by `js2-node-child-list'.
+Returns nil if no applicable child is found."
+  (let ((kids (if (js2-function-node-p parent)
+                  (js2-block-node-kids (js2-function-node-body parent))
+                (js2-node-child-list parent)))
+        (beg (js2-node-abs-pos (if (js2-function-node-p parent)
+                                   (js2-function-node-body parent)
+                                 parent)))
+        kid result fn
+        (continue t))
+    (setq fn (if after '>= '<))
+    (while (and kids continue)
+      (setq kid (car kids))
+      (if (funcall fn (+ beg (js2-node-pos kid)) pos)
+          (setq result kid
+                continue (not after))
+        (setq continue after))
+      (setq kids (cdr kids)))
+    result))
+
+(defun js2-node-find-child-after (pos parent)
+  "Find first child that starts after POS in parent.
+POS is an absolute buffer position.  PARENT is any node
+supported by `js2-node-child-list'.
+Returns nil if no applicable child is found."
+  (js2-node-find-child-before pos parent 'after))
+
+(defun js2-node-replace-child (pos parent new-node)
+  "Replace node at index POS in PARENT with NEW-NODE.
+Only works for parents supported by `js2-node-child-list'."
+  (let ((kids (js2-node-child-list parent))
+        (i 0))
+    (while (< i pos)
+      (setq kids (cdr kids)
+            i (1+ i)))
+    (setcar kids new-node)
+    (js2-node-add-children parent new-node)))
+
+(defun js2-node-buffer (n)
+  "Return the buffer associated with AST N.
+Returns nil if the buffer is not set as a property on the root
+node, or if parent links were not recorded during parsing."
+  (let ((root (js2-node-root n)))
+    (and root
+         (js2-ast-root-p root)
+         (js2-ast-root-buffer root))))
+
+(defun js2-block-node-push (n kid)
+  "Push js2-node KID onto the end of js2-block-node N's child list.
+KID is always added to the -end- of the kids list.
+Function also calls `js2-node-add-children' to add the parent link."
+  (let ((kids (js2-node-child-list n)))
+    (if kids
+        (setcdr kids (nconc (cdr kids) (list kid)))
+      (js2-node-set-child-list n (list kid)))
+    (js2-node-add-children n kid)))
+
+(defun js2-node-string (node)
+  (with-current-buffer (or (js2-node-buffer node)
+                           (error "No buffer available for node %s" node))
+    (let ((pos (js2-node-abs-pos node)))
+      (buffer-substring-no-properties pos (+ pos (js2-node-len node))))))
+
+;; Container for storing the node we're looking for in a traversal.
+(js2-deflocal js2-discovered-node nil)
+
+;; Keep track of absolute node position during traversals.
+(js2-deflocal js2-visitor-offset nil)
+
+(js2-deflocal js2-node-search-point nil)
+
+(when js2-mode-dev-mode-p
+  (defun js2-find-node-at-point ()
+    (interactive)
+    (let ((node (js2-node-at-point)))
+      (message "%s" (or node "No node found at point"))))
+  (defun js2-node-name-at-point ()
+    (interactive)
+    (let ((node (js2-node-at-point)))
+      (message "%s" (if node
+                        (js2-node-short-name node)
+                      "No node found at point.")))))
+
+(defun js2-node-at-point (&optional pos skip-comments)
+  "Return AST node at POS, a buffer position, defaulting to current point.
+The `js2-mode-ast' variable must be set to the current parse tree.
+Signals an error if the AST (`js2-mode-ast') is nil.
+Always returns a node - if it can't find one, it returns the root.
+If SKIP-COMMENTS is non-nil, comment nodes are ignored."
+  (let ((ast js2-mode-ast)
+        result)
+    (unless ast
+      (error "No JavaScript AST available"))
+    ;; Look through comments first, since they may be inside nodes that
+    ;; would otherwise report a match.
+    (setq pos (or pos (point))
+          result (if (> pos (js2-node-abs-end ast))
+                     ast
+                   (if (not skip-comments)
+                       (js2-comment-at-point pos))))
+    (unless result
+      (setq js2-discovered-node nil
+            js2-visitor-offset 0
+            js2-node-search-point pos)
+      (unwind-protect
+          (catch 'js2-visit-done
+            (js2-visit-ast ast #'js2-node-at-point-visitor))
+        (setq js2-visitor-offset nil
+              js2-node-search-point nil))
+      (setq result js2-discovered-node))
+    ;; may have found a comment beyond end of last child node,
+    ;; since visiting the ast-root looks at the comment-list last.
+    (if (and skip-comments
+             (js2-comment-node-p result))
+        (setq result nil))
+    (or result js2-mode-ast)))
+
+(defun js2-node-at-point-visitor (node end-p)
+  (let ((rel-pos (js2-node-pos node))
+        abs-pos
+        abs-end
+        (point js2-node-search-point))
+    (cond
+     (end-p
+      ;; this evaluates to a non-nil return value, even if it's zero
+      (cl-decf js2-visitor-offset rel-pos))
+     ;; we already looked for comments before visiting, and don't want them now
+     ((js2-comment-node-p node)
+      nil)
+     (t
+      (setq abs-pos (cl-incf js2-visitor-offset rel-pos)
+            ;; we only want to use the node if the point is before
+            ;; the last character position in the node, so we decrement
+            ;; the absolute end by 1.
+            abs-end (+ abs-pos (js2-node-len node) -1))
+      (cond
+       ;; If this node starts after search-point, stop the search.
+       ((> abs-pos point)
+        (throw 'js2-visit-done nil))
+       ;; If this node ends before the search-point, don't check kids.
+       ((> point abs-end)
+        nil)
+       (t
+        ;; Otherwise point is within this node, possibly in a child.
+        (setq js2-discovered-node node)
+        t))))))  ; keep processing kids to look for more specific match
+
+(defsubst js2-block-comment-p (node)
+  "Return non-nil if NODE is a comment node of format `jsdoc' or `block'."
+  (and (js2-comment-node-p node)
+       (memq (js2-comment-node-format node) '(jsdoc block))))
+
+;; TODO:  put the comments in a vector and binary-search them instead
+(defun js2-comment-at-point (&optional pos)
+  "Look through scanned comment nodes for one containing POS.
+POS is a buffer position that defaults to current point.
+Function returns nil if POS was not in any comment node."
+  (let ((ast js2-mode-ast)
+        (x (or pos (point)))
+        beg end)
+    (unless ast
+      (error "No JavaScript AST available"))
+    (catch 'done
+      ;; Comments are stored in lexical order.
+      (dolist (comment (js2-ast-root-comments ast) nil)
+        (setq beg (js2-node-abs-pos comment)
+              end (+ beg (js2-node-len comment)))
+        (if (and (>= x beg)
+                 (<= x end))
+            (throw 'done comment))))))
+
+(defun js2-mode-find-parent-fn (node)
+  "Find function enclosing NODE.
+Returns nil if NODE is not inside a function."
+  (setq node (js2-node-parent node))
+  (while (and node (not (js2-function-node-p node)))
+    (setq node (js2-node-parent node)))
+  (and (js2-function-node-p node) node))
+
+(defun js2-mode-find-enclosing-fn (node)
+  "Find function or root enclosing NODE."
+  (if (js2-ast-root-p node)
+      node
+    (setq node (js2-node-parent node))
+    (while (not (or (js2-ast-root-p node)
+                    (js2-function-node-p node)))
+      (setq node (js2-node-parent node)))
+    node))
+
+ (defun js2-mode-find-enclosing-node (beg end)
+  "Find node fully enclosing BEG and END."
+  (let ((node (js2-node-at-point beg))
+        pos
+        (continue t))
+    (while continue
+      (if (or (js2-ast-root-p node)
+              (and
+               (<= (setq pos (js2-node-abs-pos node)) beg)
+               (>= (+ pos (js2-node-len node)) end)))
+          (setq continue nil)
+        (setq node (js2-node-parent node))))
+    node))
+
+(defun js2-node-parent-script-or-fn (node)
+  "Find script or function immediately enclosing NODE.
+If NODE is the ast-root, returns nil."
+  (if (js2-ast-root-p node)
+      nil
+    (setq node (js2-node-parent node))
+    (while (and node (not (or (js2-function-node-p node)
+                              (js2-script-node-p node))))
+      (setq node (js2-node-parent node)))
+    node))
+
+(defun js2-node-is-descendant (node ancestor)
+  "Return t if NODE is a descendant of ANCESTOR."
+  (while (and node
+              (not (eq node ancestor)))
+    (setq node (js2-node-parent node)))
+  node)
+
+;;; visitor infrastructure
+
+(defun js2-visit-none (_node _callback)
+  "Visitor for AST node that have no node children."
+  nil)
+
+(defun js2-print-none (_node _indent)
+  "Visitor for AST node with no printed representation.")
+
+(defun js2-print-body (node indent)
+  "Print a statement, or a block without braces."
+  (if (js2-block-node-p node)
+      (dolist (kid (js2-block-node-kids node))
+        (js2-print-ast kid indent))
+    (js2-print-ast node indent)))
+
+(defun js2-print-list (args &optional delimiter)
+  (cl-loop with len = (length args)
+           for arg in args
+           for count from 1
+           do
+           (when arg (js2-print-ast arg 0))
+           (if (< count len)
+               (insert (or delimiter ", ")))))
+
+(defun js2-print-tree (ast)
+  "Prints an AST to the current buffer.
+Makes `js2-ast-parent-nodes' available to the printer functions."
+  (let ((max-lisp-eval-depth (max max-lisp-eval-depth 1500)))
+    (js2-print-ast ast)))
+
+(defun js2-print-ast (node &optional indent)
+  "Helper function for printing AST nodes.
+Requires `js2-ast-parent-nodes' to be non-nil.
+You should use `js2-print-tree' instead of this function."
+  (let ((printer (get (aref node 0) 'js2-printer))
+        (i (or indent 0)))
+    ;; TODO:  wedge comments in here somewhere
+    (if printer
+        (funcall printer node i))))
+
+(defconst js2-side-effecting-tokens
+  (let ((tokens (make-bool-vector js2-num-tokens nil)))
+    (dolist (tt (list js2-ASSIGN
+                      js2-ASSIGN_ADD
+                      js2-ASSIGN_BITAND
+                      js2-ASSIGN_BITOR
+                      js2-ASSIGN_BITXOR
+                      js2-ASSIGN_DIV
+                      js2-ASSIGN_LSH
+                      js2-ASSIGN_MOD
+                      js2-ASSIGN_MUL
+                      js2-ASSIGN_RSH
+                      js2-ASSIGN_SUB
+                      js2-ASSIGN_URSH
+                      js2-BLOCK
+                      js2-BREAK
+                      js2-CALL
+                      js2-CATCH
+                      js2-CATCH_SCOPE
+                      js2-CLASS
+                      js2-CONST
+                      js2-CONTINUE
+                      js2-DEBUGGER
+                      js2-DEC
+                      js2-DELPROP
+                      js2-DEL_REF
+                      js2-DO
+                      js2-ELSE
+                      js2-EMPTY
+                      js2-ENTERWITH
+                      js2-EXPORT
+                      js2-EXPR_RESULT
+                      js2-FINALLY
+                      js2-FOR
+                      js2-FUNCTION
+                      js2-GOTO
+                      js2-IF
+                      js2-IFEQ
+                      js2-IFNE
+                      js2-IMPORT
+                      js2-INC
+                      js2-JSR
+                      js2-LABEL
+                      js2-LEAVEWITH
+                      js2-LET
+                      js2-LETEXPR
+                      js2-LOCAL_BLOCK
+                      js2-LOOP
+                      js2-NEW
+                      js2-REF_CALL
+                      js2-RETHROW
+                      js2-RETURN
+                      js2-RETURN_RESULT
+                      js2-SEMI
+                      js2-SETELEM
+                      js2-SETELEM_OP
+                      js2-SETNAME
+                      js2-SETPROP
+                      js2-SETPROP_OP
+                      js2-SETVAR
+                      js2-SET_REF
+                      js2-SET_REF_OP
+                      js2-SWITCH
+                      js2-TARGET
+                      js2-THROW
+                      js2-TRY
+                      js2-VAR
+                      js2-WHILE
+                      js2-WITH
+                      js2-WITHEXPR
+                      js2-YIELD))
+      (aset tokens tt t))
+    (if js2-instanceof-has-side-effects
+        (aset tokens js2-INSTANCEOF t))
+    tokens))
+
+(defun js2-node-has-side-effects (node)
+  "Return t if NODE has side effects."
+  (when node  ; makes it easier to handle malformed expressions
+    (let ((tt (js2-node-type node)))
+      (cond
+       ;; This doubtless needs some work, since EXPR_VOID is used
+       ;; in several ways in Rhino and I may not have caught them all.
+       ;; I'll wait for people to notice incorrect warnings.
+       ((and (= tt js2-EXPR_VOID)
+             (js2-expr-stmt-node-p node)) ; but not if EXPR_RESULT
+        (let ((expr (js2-expr-stmt-node-expr node)))
+          (or (js2-node-has-side-effects expr)
+              (when (js2-string-node-p expr)
+                (member (js2-string-node-value expr) '("use strict" "use asm"))))))
+       ((= tt js2-COMMA)
+        (js2-node-has-side-effects (js2-infix-node-right node)))
+       ((or (= tt js2-AND)
+            (= tt js2-OR))
+        (or (js2-node-has-side-effects (js2-infix-node-right node))
+            (js2-node-has-side-effects (js2-infix-node-left node))))
+       ((= tt js2-HOOK)
+        (and (js2-node-has-side-effects (js2-cond-node-true-expr node))
+             (js2-node-has-side-effects (js2-cond-node-false-expr node))))
+       ((js2-paren-node-p node)
+        (js2-node-has-side-effects (js2-paren-node-expr node)))
+       ((= tt js2-ERROR) ; avoid cascaded error messages
+        nil)
+       (t
+        (aref js2-side-effecting-tokens tt))))))
+
+(defconst js2-stmt-node-types
+  (list js2-BLOCK
+        js2-BREAK
+        js2-CONTINUE
+        js2-DEFAULT  ; e4x "default xml namespace" statement
+        js2-DO
+        js2-EXPORT
+        js2-EXPR_RESULT
+        js2-EXPR_VOID
+        js2-FOR
+        js2-IF
+        js2-IMPORT
+        js2-RETURN
+        js2-SWITCH
+        js2-THROW
+        js2-TRY
+        js2-WHILE
+        js2-WITH)
+  "Node types that only appear in statement contexts.
+The list does not include nodes that always appear as the child
+of another specific statement type, such as switch-cases,
+catch and finally blocks, and else-clauses.  The list also excludes
+nodes like yield, let and var, which may appear in either expression
+or statement context, and in the latter context always have a
+`js2-expr-stmt-node' parent.  Finally, the list does not include
+functions or scripts, which are treated separately from statements
+by the JavaScript parser and runtime.")
+
+(defun js2-stmt-node-p (node)
+  "Heuristic for figuring out if NODE is a statement.
+Some node types can appear in either an expression context or a
+statement context, e.g. let-nodes, yield-nodes, and var-decl nodes.
+For these node types in a statement context, the parent will be a
+`js2-expr-stmt-node'.
+Functions aren't included in the check."
+  (memq (js2-node-type node) js2-stmt-node-types))
+
+(defun js2-mode-find-first-stmt (node)
+  "Search upward starting from NODE looking for a statement.
+For purposes of this function, a `js2-function-node' counts."
+  (while (not (or (js2-stmt-node-p node)
+                  (js2-function-node-p node)))
+    (setq node (js2-node-parent node)))
+  node)
+
+(defun js2-node-parent-stmt (node)
+  "Return the node's first ancestor that is a statement.
+Returns nil if NODE is a `js2-ast-root'.  Note that any expression
+appearing in a statement context will have a parent that is a
+`js2-expr-stmt-node' that will be returned by this function."
+  (let ((parent (js2-node-parent node)))
+    (if (or (null parent)
+            (js2-stmt-node-p parent)
+            (and (js2-function-node-p parent)
+                 (not (eq (js2-function-node-form parent)
+                          'FUNCTION_EXPRESSION))))
+        parent
+      (js2-node-parent-stmt parent))))
+
+;; In the Mozilla Rhino sources, Roshan James writes:
+;;  Does consistent-return analysis on the function body when strict mode is
+;;  enabled.
+;;
+;;    function (x) { return (x+1) }
+;;
+;;  is ok, but
+;;
+;;    function (x) { if (x < 0) return (x+1); }
+;;
+;;  is not because the function can potentially return a value when the
+;;  condition is satisfied and if not, the function does not explicitly
+;;  return a value.
+;;
+;;  This extends to checking mismatches such as "return" and "return <value>"
+;;  used in the same function. Warnings are not emitted if inconsistent
+;;  returns exist in code that can be statically shown to be unreachable.
+;;  Ex.
+;;    function (x) { while (true) { ... if (..) { return value } ... } }
+;;
+;;  emits no warning. However if the loop had a break statement, then a
+;;  warning would be emitted.
+;;
+;;  The consistency analysis looks at control structures such as loops, ifs,
+;;  switch, try-catch-finally blocks, examines the reachable code paths and
+;;  warns the user about an inconsistent set of termination possibilities.
+;;
+;;  These flags enumerate the possible ways a statement/function can
+;;  terminate. These flags are used by endCheck() and by the Parser to
+;;  detect inconsistent return usage.
+;;
+;;  END_UNREACHED is reserved for code paths that are assumed to always be
+;;  able to execute (example: throw, continue)
+;;
+;;  END_DROPS_OFF indicates if the statement can transfer control to the
+;;  next one. Statement such as return dont. A compound statement may have
+;;  some branch that drops off control to the next statement.
+;;
+;;  END_RETURNS indicates that the statement can return with no value.
+;;  END_RETURNS_VALUE indicates that the statement can return a value.
+;;
+;;  A compound statement such as
+;;  if (condition) {
+;;    return value;
+;;  }
+;;  Will be detected as (END_DROPS_OFF | END_RETURN_VALUE) by endCheck()
+
+(defconst js2-END_UNREACHED 0)
+(defconst js2-END_DROPS_OFF 1)
+(defconst js2-END_RETURNS 2)
+(defconst js2-END_RETURNS_VALUE 4)
+(defconst js2-END_YIELDS 8)
+
+(defun js2-has-consistent-return-usage (node)
+  "Check that every return usage in a function body is consistent.
+Returns t if the function satisfies strict mode requirement."
+  (let ((n (js2-end-check node)))
+    ;; either it doesn't return a value in any branch...
+    (or (js2-flag-not-set-p n js2-END_RETURNS_VALUE)
+        ;; or it returns a value (or is unreached) at every branch
+        (js2-flag-not-set-p n (logior js2-END_DROPS_OFF
+                                      js2-END_RETURNS
+                                      js2-END_YIELDS)))))
+
+(defun js2-end-check-if (node)
+  "Ensure that return usage in then/else blocks is consistent.
+If there is no else block, then the return statement can fall through.
+Returns logical OR of END_* flags"
+  (let ((th (js2-if-node-then-part node))
+        (el (js2-if-node-else-part node)))
+    (if (null th)
+        js2-END_UNREACHED
+      (logior (js2-end-check th) (if el
+                                     (js2-end-check el)
+                                   js2-END_DROPS_OFF)))))
+
+(defun js2-end-check-switch (node)
+  "Consistency of return statements is checked between the case statements.
+If there is no default, then the switch can fall through. If there is a
+default, we check to see if all code paths in the default return or if
+there is a code path that can fall through.
+Returns logical OR of END_* flags."
+  (let ((rv js2-END_UNREACHED)
+        default-case)
+    ;; examine the cases
+    (catch 'break
+      (dolist (c (js2-switch-node-cases node))
+        (if (js2-case-node-expr c)
+            (js2-set-flag rv (js2-end-check-block c))
+          (setq default-case c)
+          (throw 'break nil))))
+    ;; we don't care how the cases drop into each other
+    (js2-clear-flag rv js2-END_DROPS_OFF)
+    ;; examine the default
+    (js2-set-flag rv (if default-case
+                         (js2-end-check default-case)
+                       js2-END_DROPS_OFF))
+    rv))
+
+(defun js2-end-check-try (node)
+ "If the block has a finally, return consistency is checked in the
+finally block. If all code paths in the finally return, then the
+returns in the try-catch blocks don't matter. If there is a code path
+that does not return or if there is no finally block, the returns
+of the try and catch blocks are checked for mismatch.
+Returns logical OR of END_* flags."
+ (let ((finally (js2-try-node-finally-block node))
+       rv)
+   ;; check the finally if it exists
+   (setq rv (if finally
+                (js2-end-check (js2-finally-node-body finally))
+              js2-END_DROPS_OFF))
+   ;; If the finally block always returns, then none of the returns
+   ;; in the try or catch blocks matter.
+   (when (js2-flag-set-p rv js2-END_DROPS_OFF)
+     (js2-clear-flag rv js2-END_DROPS_OFF)
+     ;; examine the try block
+     (js2-set-flag rv (js2-end-check (js2-try-node-try-block node)))
+     ;; check each catch block
+     (dolist (cb (js2-try-node-catch-clauses node))
+       (js2-set-flag rv (js2-end-check cb))))
+   rv))
+
+(defun js2-end-check-loop (node)
+  "Return statement in the loop body must be consistent.
+The default assumption for any kind of a loop is that it will eventually
+terminate.  The only exception is a loop with a constant true condition.
+Code that follows such a loop is examined only if one can determine
+statically that there is a break out of the loop.
+
+    for(... ; ... ; ...) {}
+    for(... in ... ) {}
+    while(...) { }
+    do { } while(...)
+
+Returns logical OR of END_* flags."
+  (let ((rv (js2-end-check (js2-loop-node-body node)))
+        (condition (cond
+                    ((js2-while-node-p node)
+                     (js2-while-node-condition node))
+                     ((js2-do-node-p node)
+                      (js2-do-node-condition node))
+                     ((js2-for-node-p node)
+                      (js2-for-node-condition node)))))
+
+    ;; check to see if the loop condition is always true
+    (if (and condition
+             (eq (js2-always-defined-boolean-p condition) 'ALWAYS_TRUE))
+        (js2-clear-flag rv js2-END_DROPS_OFF))
+
+    ;; look for effect of breaks
+    (js2-set-flag rv (js2-node-get-prop node
+                                        'CONTROL_BLOCK_PROP
+                                        js2-END_UNREACHED))
+    rv))
+
+(defun js2-end-check-block (node)
+  "A general block of code is examined statement by statement.
+If any statement (even a compound one) returns in all branches, then
+subsequent statements are not examined.
+Returns logical OR of END_* flags."
+  (let* ((rv js2-END_DROPS_OFF)
+         (kids (js2-block-node-kids node))
+         (n (car kids)))
+    ;; Check each statment.  If the statement can continue onto the next
+    ;; one (i.e. END_DROPS_OFF is set), then check the next statement.
+    (while (and n (js2-flag-set-p rv js2-END_DROPS_OFF))
+      (js2-clear-flag rv js2-END_DROPS_OFF)
+      (js2-set-flag rv (js2-end-check n))
+      (setq kids (cdr kids)
+            n (car kids)))
+    rv))
+
+(defun js2-end-check-label (node)
+  "A labeled statement implies that there may be a break to the label.
+The function processes the labeled statement and then checks the
+CONTROL_BLOCK_PROP property to see if there is ever a break to the
+particular label.
+Returns logical OR of END_* flags."
+  (let ((rv (js2-end-check (js2-labeled-stmt-node-stmt node))))
+    (logior rv (js2-node-get-prop node
+                                  'CONTROL_BLOCK_PROP
+                                  js2-END_UNREACHED))))
+
+(defun js2-end-check-break (node)
+  "When a break is encountered annotate the statement being broken
+out of by setting its CONTROL_BLOCK_PROP property.
+Returns logical OR of END_* flags."
+  (and (js2-break-node-target node)
+       (js2-node-set-prop (js2-break-node-target node)
+                          'CONTROL_BLOCK_PROP
+                          js2-END_DROPS_OFF))
+  js2-END_UNREACHED)
+
+(defun js2-end-check (node)
+  "Examine the body of a function, doing a basic reachability analysis.
+Returns a combination of flags END_* flags that indicate
+how the function execution can terminate. These constitute only the
+pessimistic set of termination conditions. It is possible that at
+runtime certain code paths will never be actually taken. Hence this
+analysis will flag errors in cases where there may not be errors.
+Returns logical OR of END_* flags"
+  (let (kid)
+    (cond
+     ((js2-break-node-p node)
+      (js2-end-check-break node))
+     ((js2-expr-stmt-node-p node)
+      (if (setq kid (js2-expr-stmt-node-expr node))
+          (js2-end-check kid)
+        js2-END_DROPS_OFF))
+     ((or (js2-continue-node-p node)
+          (js2-throw-node-p node))
+      js2-END_UNREACHED)
+     ((js2-return-node-p node)
+      (if (setq kid (js2-return-node-retval node))
+          js2-END_RETURNS_VALUE
+        js2-END_RETURNS))
+     ((js2-loop-node-p node)
+      (js2-end-check-loop node))
+     ((js2-switch-node-p node)
+      (js2-end-check-switch node))
+     ((js2-labeled-stmt-node-p node)
+      (js2-end-check-label node))
+     ((js2-if-node-p node)
+      (js2-end-check-if node))
+     ((js2-try-node-p node)
+      (js2-end-check-try node))
+     ((js2-block-node-p node)
+      (if (null (js2-block-node-kids node))
+          js2-END_DROPS_OFF
+        (js2-end-check-block node)))
+     ((js2-yield-node-p node)
+      js2-END_YIELDS)
+     (t
+      js2-END_DROPS_OFF))))
+
+(defun js2-always-defined-boolean-p (node)
+  "Check if NODE always evaluates to true or false in boolean context.
+Returns 'ALWAYS_TRUE, 'ALWAYS_FALSE, or nil if it's neither always true
+nor always false."
+  (let ((tt (js2-node-type node))
+        num)
+    (cond
+     ((or (= tt js2-FALSE) (= tt js2-NULL))
+      'ALWAYS_FALSE)
+     ((= tt js2-TRUE)
+      'ALWAYS_TRUE)
+     ((= tt js2-NUMBER)
+      (setq num (js2-number-node-num-value node))
+      (if (and (not (eq num 0.0e+NaN))
+               (not (zerop num)))
+          'ALWAYS_TRUE
+        'ALWAYS_FALSE))
+     (t
+      nil))))
+
+;;; Scanner -- a port of Mozilla Rhino's lexer.
+;; Corresponds to Rhino files Token.java and TokenStream.java.
+
+(defvar js2-tokens nil
+  "List of all defined token names.")  ; initialized in `js2-token-names'
+
+(defconst js2-token-names
+  (let* ((names (make-vector js2-num-tokens -1))
+         (case-fold-search nil)  ; only match js2-UPPER_CASE
+         (syms (apropos-internal "^js2-\\(?:[[:upper:]_]+\\)")))
+    (cl-loop for sym in syms
+             for i from 0
+             do
+             (unless (or (memq sym '(js2-EOF_CHAR js2-ERROR))
+                         (not (boundp sym)))
+               (aset names (symbol-value sym)           ; code, e.g. 152
+                     (downcase
+                      (substring (symbol-name sym) 4))) ; name, e.g. "let"
+               (push sym js2-tokens)))
+    names)
+  "Vector mapping int values to token string names, sans `js2-' prefix.")
+
+(defun js2-tt-name (tok)
+  "Return a string name for TOK, a token symbol or code.
+Signals an error if it's not a recognized token."
+  (let ((code tok))
+    (if (symbolp tok)
+        (setq code (symbol-value tok)))
+    (if (eq code -1)
+        "ERROR"
+      (if (and (numberp code)
+               (not (cl-minusp code))
+               (< code js2-num-tokens))
+          (aref js2-token-names code)
+        (error "Invalid token: %s" code)))))
+
+(defsubst js2-tt-sym (tok)
+  "Return symbol for TOK given its code, e.g. 'js2-LP for code 86."
+  (intern (js2-tt-name tok)))
+
+(defconst js2-token-codes
+  (let ((table (make-hash-table :test 'eq :size 256)))
+    (cl-loop for name across js2-token-names
+             for sym = (intern (concat "js2-" (upcase name)))
+             do
+             (puthash sym (symbol-value sym) table))
+    ;; clean up a few that are "wrong" in Rhino's token codes
+    (puthash 'js2-DELETE js2-DELPROP table)
+    table)
+  "Hashtable mapping token type symbols to their bytecodes.")
+
+(defsubst js2-tt-code (sym)
+  "Return code for token symbol SYM, e.g. 86 for 'js2-LP."
+  (or (gethash sym js2-token-codes)
+      (error "Invalid token symbol: %s " sym)))  ; signal code bug
+
+(defun js2-report-scan-error (msg &optional no-throw beg len)
+  (setf (js2-token-end (js2-current-token)) js2-ts-cursor)
+  (js2-report-error msg nil
+                    (or beg (js2-current-token-beg))
+                    (or len (js2-current-token-len)))
+  (unless no-throw
+    (throw 'return js2-ERROR)))
+
+(defun js2-set-string-from-buffer (token)
+  "Set `string' and `end' slots for TOKEN, return the string."
+  (setf (js2-token-end token) js2-ts-cursor
+        (js2-token-string token) (js2-collect-string js2-ts-string-buffer)))
+
+;; TODO:  could potentially avoid a lot of consing by allocating a
+;; char buffer the way Rhino does.
+(defsubst js2-add-to-string (c)
+  (push c js2-ts-string-buffer))
+
+;; Note that when we "read" the end-of-file, we advance js2-ts-cursor
+;; to (1+ (point-max)), which lets the scanner treat end-of-file like
+;; any other character:  when it's not part of the current token, we
+;; unget it, allowing it to be read again by the following call.
+(defsubst js2-unget-char ()
+  (cl-decf js2-ts-cursor))
+
+;; Rhino distinguishes \r and \n line endings.  We don't need to
+;; because we only scan from Emacs buffers, which always use \n.
+(defun js2-get-char ()
+  "Read and return the next character from the input buffer.
+Increments `js2-ts-lineno' if the return value is a newline char.
+Updates `js2-ts-cursor' to the point after the returned char.
+Returns `js2-EOF_CHAR' if we hit the end of the buffer.
+Also updates `js2-ts-hit-eof' and `js2-ts-line-start' as needed."
+  (let (c)
+    ;; check for end of buffer
+    (if (>= js2-ts-cursor (point-max))
+        (setq js2-ts-hit-eof t
+              js2-ts-cursor (1+ js2-ts-cursor)
+              c js2-EOF_CHAR)  ; return value
+      ;; otherwise read next char
+      (setq c (char-before (cl-incf js2-ts-cursor)))
+      ;; if we read a newline, update counters
+      (if (= c ?\n)
+          (setq js2-ts-line-start js2-ts-cursor
+                js2-ts-lineno (1+ js2-ts-lineno)))
+      ;; TODO:  skip over format characters
+      c)))
+
+(defun js2-read-unicode-escape ()
+  "Read a \\uNNNN sequence from the input.
+Assumes the ?\ and ?u have already been read.
+Returns the unicode character, or nil if it wasn't a valid character.
+Doesn't change the values of any scanner variables."
+  ;; I really wish I knew a better way to do this, but I can't
+  ;; find the Emacs function that takes a 16-bit int and converts
+  ;; it to a Unicode/utf-8 character.  So I basically eval it with (read).
+  ;; Have to first check that it's 4 hex characters or it may stop
+  ;; the read early.
+  (ignore-errors
+    (let ((s (buffer-substring-no-properties js2-ts-cursor
+                                             (+ 4 js2-ts-cursor))))
+      (if (string-match "[0-9a-fA-F]\\{4\\}" s)
+          (read (concat "?\\u" s))))))
+
+(defun js2-match-char (test)
+  "Consume and return next character if it matches TEST, a character.
+Returns nil and consumes nothing if TEST is not the next character."
+  (let ((c (js2-get-char)))
+    (if (eq c test)
+        t
+      (js2-unget-char)
+      nil)))
+
+(defun js2-peek-char ()
+  (prog1
+      (js2-get-char)
+    (js2-unget-char)))
+
+(defun js2-identifier-start-p (c)
+  "Is C a valid start to an ES5 Identifier?
+See http://es5.github.io/#x7.6"
+  (or
+   (memq c '(?$ ?_))
+   (memq (get-char-code-property c 'general-category)
+         ;; Letters
+         '(Lu Ll Lt Lm Lo Nl))))
+
+(defun js2-identifier-part-p (c)
+  "Is C a valid part of an ES5 Identifier?
+See http://es5.github.io/#x7.6"
+  (or
+   (memq c '(?$ ?_ ?\u200c  ?\u200d))
+   (memq (get-char-code-property c 'general-category)
+         '(;; Letters
+           Lu Ll Lt Lm Lo Nl
+           ;; Combining Marks
+           Mn Mc
+           ;; Digits
+           Nd
+           ;; Connector Punctuation
+           Pc))))
+
+(defun js2-alpha-p (c)
+  (cond ((and (<= ?A c) (<= c ?Z)) t)
+        ((and (<= ?a c) (<= c ?z)) t)
+        (t nil)))
+
+(defsubst js2-digit-p (c)
+  (and (<= ?0 c) (<= c ?9)))
+
+(defun js2-js-space-p (c)
+  (if (<= c 127)
+      (memq c '(#x20 #x9 #xB #xC #xD))
+    (or
+     (eq c #xA0)
+     ;; TODO:  change this nil to check for Unicode space character
+     nil)))
+
+(defconst js2-eol-chars (list js2-EOF_CHAR ?\n ?\r))
+
+(defun js2-skip-line ()
+  "Skip to end of line."
+  (while (not (memq (js2-get-char) js2-eol-chars)))
+  (js2-unget-char)
+  (setf (js2-token-end (js2-current-token)) js2-ts-cursor)
+  (setq js2-token-end js2-ts-cursor))
+
+(defun js2-init-scanner (&optional buf line)
+  "Create token stream for BUF starting on LINE.
+BUF defaults to `current-buffer' and LINE defaults to 1.
+
+A buffer can only have one scanner active at a time, which yields
+dramatically simpler code than using a defstruct.  If you need to
+have simultaneous scanners in a buffer, copy the regions to scan
+into temp buffers."
+  (with-current-buffer (or buf (current-buffer))
+    (setq js2-ts-dirty-line nil
+          js2-ts-hit-eof nil
+          js2-ts-line-start 0
+          js2-ts-lineno (or line 1)
+          js2-ts-line-end-char -1
+          js2-ts-cursor (point-min)
+          js2-ti-tokens (make-vector js2-ti-ntokens nil)
+          js2-ti-tokens-cursor 0
+          js2-ti-lookahead 0
+          js2-ts-is-xml-attribute nil
+          js2-ts-xml-is-tag-content nil
+          js2-ts-xml-open-tags-count 0
+          js2-ts-string-buffer nil)))
+
+;; This function uses the cached op, string and number fields in
+;; TokenStream; if getToken has been called since the passed token
+;; was scanned, the op or string printed may be incorrect.
+(defun js2-token-to-string (token)
+  ;; Not sure where this function is used in Rhino.  Not tested.
+  (if (not js2-debug-print-trees)
+      ""
+    (let ((name (js2-tt-name token)))
+      (cond
+       ((memq token '(js2-STRING js2-REGEXP js2-NAME
+                      js2-TEMPLATE_HEAD js2-NO_SUBS_TEMPLATE))
+        (concat name " `" (js2-current-token-string) "'"))
+       ((eq token js2-NUMBER)
+        (format "NUMBER %g" (js2-token-number (js2-current-token))))
+       (t
+        name)))))
+
+(defconst js2-keywords
+  '(break
+    case catch class const continue
+    debugger default delete do
+    else extends export
+    false finally for function
+    if in instanceof import
+    let
+    new null
+    return
+    static super switch
+    this throw true try typeof
+    var void
+    while with
+    yield))
+
+;; Token names aren't exactly the same as the keywords, unfortunately.
+;; E.g. delete is js2-DELPROP.
+(defconst js2-kwd-tokens
+  (let ((table (make-vector js2-num-tokens nil))
+        (tokens
+         (list js2-BREAK
+               js2-CASE js2-CATCH js2-CLASS js2-CONST js2-CONTINUE
+               js2-DEBUGGER js2-DEFAULT js2-DELPROP js2-DO
+               js2-ELSE js2-EXPORT
+               js2-ELSE js2-EXTENDS js2-EXPORT
+               js2-FALSE js2-FINALLY js2-FOR js2-FUNCTION
+               js2-IF js2-IN js2-INSTANCEOF js2-IMPORT
+               js2-LET
+               js2-NEW js2-NULL
+               js2-RETURN
+               js2-STATIC js2-SUPER js2-SWITCH
+               js2-THIS js2-THROW js2-TRUE js2-TRY js2-TYPEOF
+               js2-VAR
+               js2-WHILE js2-WITH
+               js2-YIELD)))
+    (dolist (i tokens)
+      (aset table i 'font-lock-keyword-face))
+    (aset table js2-STRING 'font-lock-string-face)
+    (aset table js2-REGEXP 'font-lock-string-face)
+    (aset table js2-NO_SUBS_TEMPLATE 'font-lock-string-face)
+    (aset table js2-TEMPLATE_HEAD 'font-lock-string-face)
+    (aset table js2-COMMENT 'font-lock-comment-face)
+    (aset table js2-THIS 'font-lock-builtin-face)
+    (aset table js2-SUPER 'font-lock-builtin-face)
+    (aset table js2-VOID 'font-lock-constant-face)
+    (aset table js2-NULL 'font-lock-constant-face)
+    (aset table js2-TRUE 'font-lock-constant-face)
+    (aset table js2-FALSE 'font-lock-constant-face)
+    (aset table js2-NOT 'font-lock-negation-char-face)
+    table)
+  "Vector whose values are non-nil for tokens that are keywords.
+The values are default faces to use for highlighting the keywords.")
+
+;; FIXME: Support strict mode-only future reserved words, after we know
+;; which parts scopes are in strict mode, and which are not.
+(defconst js2-reserved-words '(class enum export extends import super)
+  "Future reserved keywords in ECMAScript 5.1.")
+
+(defconst js2-keyword-names
+  (let ((table (make-hash-table :test 'equal)))
+    (cl-loop for k in js2-keywords
+             do (puthash
+                 (symbol-name k)                            ; instanceof
+                 (intern (concat "js2-"
+                                 (upcase (symbol-name k)))) ; js2-INSTANCEOF
+                 table))
+    table)
+  "JavaScript keywords by name, mapped to their symbols.")
+
+(defconst js2-reserved-word-names
+  (let ((table (make-hash-table :test 'equal)))
+    (cl-loop for k in js2-reserved-words
+             do
+             (puthash (symbol-name k) 'js2-RESERVED table))
+    table)
+  "JavaScript reserved words by name, mapped to 'js2-RESERVED.")
+
+(defun js2-collect-string (buf)
+  "Convert BUF, a list of chars, to a string.
+Reverses BUF before converting."
+  (if buf
+      (apply #'string (nreverse buf))
+    ""))
+
+(defun js2-string-to-keyword (s)
+  "Return token for S, a string, if S is a keyword or reserved word.
+Returns a symbol such as 'js2-BREAK, or nil if not keyword/reserved."
+  (or (gethash s js2-keyword-names)
+      (gethash s js2-reserved-word-names)))
+
+(defsubst js2-ts-set-char-token-bounds (token)
+  "Used when next token is one character."
+  (setf (js2-token-beg token) (1- js2-ts-cursor)
+        (js2-token-end token) js2-ts-cursor))
+
+(defsubst js2-ts-return (token type)
+  "Update the `end' and `type' slots of TOKEN,
+then throw `return' with value TYPE."
+  (setf (js2-token-end token) js2-ts-cursor
+        (js2-token-type token) type)
+  (throw 'return type))
+
+(defun js2-x-digit-to-int (c accumulator)
+  "Build up a hex number.
+If C is a hexadecimal digit, return ACCUMULATOR * 16 plus
+corresponding number.  Otherwise return -1."
+  (catch 'return
+    (catch 'check
+      ;; Use 0..9 < A..Z < a..z
+      (cond
+       ((<= c ?9)
+        (cl-decf c ?0)
+        (if (<= 0 c)
+            (throw 'check nil)))
+       ((<= c ?F)
+        (when (<= ?A c)
+          (cl-decf c (- ?A 10))
+          (throw 'check nil)))
+       ((<= c ?f)
+        (when (<= ?a c)
+          (cl-decf c (- ?a 10))
+          (throw 'check nil))))
+      (throw 'return -1))
+    (logior c (lsh accumulator 4))))
+
+(defun js2-get-token (&optional modifier)
+  "If `js2-ti-lookahead' is zero, call scanner to get new token.
+Otherwise, move `js2-ti-tokens-cursor' and return the type of
+next saved token.
+
+This function will not return a newline (js2-EOL) - instead, it
+gobbles newlines until it finds a non-newline token.  Call
+`js2-peek-token-or-eol' when you care about newlines.
+
+This function will also not return a js2-COMMENT.  Instead, it
+records comments found in `js2-scanned-comments'.  If the token
+returned by this function immediately follows a jsdoc comment,
+the token is flagged as such."
+  (if (zerop js2-ti-lookahead)
+      (js2-get-token-internal modifier)
+    (cl-decf js2-ti-lookahead)
+    (setq js2-ti-tokens-cursor (mod (1+ js2-ti-tokens-cursor) js2-ti-ntokens))
+    (let ((tt (js2-current-token-type)))
+      (cl-assert (not (= tt js2-EOL)))
+      tt)))
+
+(defun js2-unget-token ()
+  (cl-assert (< js2-ti-lookahead js2-ti-max-lookahead))
+  (cl-incf js2-ti-lookahead)
+  (setq js2-ti-tokens-cursor (mod (1- js2-ti-tokens-cursor) js2-ti-ntokens)))
+
+(defun js2-get-token-internal (modifier)
+  (let* ((token (js2-get-token-internal-1 modifier)) ; call scanner
+         (tt (js2-token-type token))
+         saw-eol
+         face)
+    ;; process comments
+    (while (or (= tt js2-EOL) (= tt js2-COMMENT))
+      (if (= tt js2-EOL)
+          (setq saw-eol t)
+        (setq saw-eol nil)
+        (when js2-record-comments
+          (js2-record-comment token)))
+      (setq js2-ti-tokens-cursor (mod (1- js2-ti-tokens-cursor) js2-ti-ntokens))
+      (setq token (js2-get-token-internal-1 modifier) ; call scanner again
+            tt (js2-token-type token)))
+
+    (when saw-eol
+      (setf (js2-token-follows-eol-p token) t))
+
+    ;; perform lexical fontification as soon as token is scanned
+    (when js2-parse-ide-mode
+      (cond
+       ((cl-minusp tt)
+        (js2-record-face 'js2-error token))
+       ((setq face (aref js2-kwd-tokens tt))
+        (js2-record-face face token))
+       ((and (= tt js2-NAME)
+             (equal (js2-token-string token) "undefined"))
+        (js2-record-face 'font-lock-constant-face token))))
+    tt))
+
+(defsubst js2-string-to-number (str base)
+  ;; TODO:  Maybe port ScriptRuntime.stringToNumber.
+  (condition-case nil
+      (string-to-number str base)
+    (overflow-error -1)))
+
+(defun js2-get-token-internal-1 (modifier)
+  "Return next JavaScript token type, an int such as js2-RETURN.
+During operation, creates an instance of `js2-token' struct, sets
+its relevant fields and puts it into `js2-ti-tokens'."
+  (let (identifier-start
+        is-unicode-escape-start c c1
+        contains-escape escape-val str result base
+        quote-char val look-for-slash continue tt
+        (token (js2-new-token 0)))
+    (setq
+     tt
+     (catch 'return
+       (when (eq modifier 'TEMPLATE_TAIL)
+         (setf (js2-token-beg token) (1- js2-ts-cursor))
+         (throw 'return (js2-get-string-or-template-token ?` token)))
+       (while t
+         ;; Eat whitespace, possibly sensitive to newlines.
+         (setq continue t)
+         (while continue
+           (setq c (js2-get-char))
+           (cond
+            ((eq c js2-EOF_CHAR)
+             (js2-unget-char)
+             (js2-ts-set-char-token-bounds token)
+             (throw 'return js2-EOF))
+            ((eq c ?\n)
+             (js2-ts-set-char-token-bounds token)
+             (setq js2-ts-dirty-line nil)
+             (throw 'return js2-EOL))
+            ((not (js2-js-space-p c))
+             (if (/= c ?-)              ; in case end of HTML comment
+                 (setq js2-ts-dirty-line t))
+             (setq continue nil))))
+         ;; Assume the token will be 1 char - fixed up below.
+         (js2-ts-set-char-token-bounds token)
+         (when (eq c ?@)
+           (throw 'return js2-XMLATTR))
+         ;; identifier/keyword/instanceof?
+         ;; watch out for starting with a <backslash>
+         (cond
+          ((eq c ?\\)
+           (setq c (js2-get-char))
+           (if (eq c ?u)
+               (setq identifier-start t
+                     is-unicode-escape-start t
+                     js2-ts-string-buffer nil)
+             (setq identifier-start nil)
+             (js2-unget-char)
+             (setq c ?\\)))
+          (t
+           (when (setq identifier-start (js2-identifier-start-p c))
+             (setq js2-ts-string-buffer nil)
+             (js2-add-to-string c))))
+         (when identifier-start
+           (setq contains-escape is-unicode-escape-start)
+           (catch 'break
+             (while t
+               (if is-unicode-escape-start
+                   ;; strictly speaking we should probably push-back
+                   ;; all the bad characters if the <backslash>uXXXX
+                   ;; sequence is malformed. But since there isn't a
+                   ;; correct context(is there?) for a bad Unicode
+                   ;; escape sequence in an identifier, we can report
+                   ;; an error here.
+                   (progn
+                     (setq escape-val 0)
+                     (dotimes (_ 4)
+                       (setq c (js2-get-char)
+                             escape-val (js2-x-digit-to-int c escape-val))
+                       ;; Next check takes care of c < 0 and bad escape
+                       (if (cl-minusp escape-val)
+                           (throw 'break nil)))
+                     (if (cl-minusp escape-val)
+                         (js2-report-scan-error "msg.invalid.escape" t))
+                     (js2-add-to-string escape-val)
+                     (setq is-unicode-escape-start nil))
+                 (setq c (js2-get-char))
+                 (cond
+                  ((eq c ?\\)
+                   (setq c (js2-get-char))
+                   (if (eq c ?u)
+                       (setq is-unicode-escape-start t
+                             contains-escape t)
+                     (js2-report-scan-error "msg.illegal.character" t)))
+                  (t
+                   (if (or (eq c js2-EOF_CHAR)
+                           (not (js2-identifier-part-p c)))
+                       (throw 'break nil))
+                   (js2-add-to-string c))))))
+           (js2-unget-char)
+           (setf str (js2-collect-string js2-ts-string-buffer)
+                 (js2-token-end token) js2-ts-cursor)
+           ;; FIXME: Invalid in ES5 and ES6, see
+           ;; https://bugzilla.mozilla.org/show_bug.cgi?id=694360
+           ;; Probably should just drop this conditional.
+           (unless contains-escape
+             ;; OPT we shouldn't have to make a string (object!) to
+             ;; check if it's a keyword.
+             ;; Return the corresponding token if it's a keyword
+             (when (and (not (eq modifier 'KEYWORD_IS_NAME))
+                        (setq result (js2-string-to-keyword str)))
+               (if (and (< js2-language-version 170)
+                        (memq result '(js2-LET js2-YIELD)))
+                   ;; LET and YIELD are tokens only in 1.7 and later
+                   (setq result 'js2-NAME))
+               (when (eq result 'js2-RESERVED)
+                 (setf (js2-token-string token) str))
+               (throw 'return (js2-tt-code result))))
+           ;; If we want to intern these as Rhino does, just use (intern str)
+           (setf (js2-token-string token) str)
+           (throw 'return js2-NAME))    ; end identifier/kwd check
+         ;; is it a number?
+         (when (or (js2-digit-p c)
+                   (and (eq c ?.) (js2-digit-p (js2-peek-char))))
+           (setq js2-ts-string-buffer nil
+                 base 10)
+           (when (eq c ?0)
+             (setq c (js2-get-char))
+             (cond
+              ((or (eq c ?x) (eq c ?X))
+               (setq base 16)
+               (setq c (js2-get-char)))
+              ((and (or (eq c ?b) (eq c ?B))
+                    (>= js2-language-version 200))
+               (setq base 2)
+               (setq c (js2-get-char)))
+              ((and (or (eq c ?o) (eq c ?O))
+                    (>= js2-language-version 200))
+               (setq base 8)
+               (setq c (js2-get-char)))
+              ((js2-digit-p c)
+               (setq base 'maybe-8))
+              (t
+               (js2-add-to-string ?0))))
+           (cond
+            ((eq base 16)
+             (if (> 0 (js2-x-digit-to-int c 0))
+                 (js2-report-scan-error "msg.missing.hex.digits")
+               (while (<= 0 (js2-x-digit-to-int c 0))
+                 (js2-add-to-string c)
+                 (setq c (js2-get-char)))))
+            ((eq base 2)
+             (if (not (memq c '(?0 ?1)))
+                 (js2-report-scan-error "msg.missing.binary.digits")
+               (while (memq c '(?0 ?1))
+                 (js2-add-to-string c)
+                 (setq c (js2-get-char)))))
+            ((eq base 8)
+             (if (or (> ?0 c) (< ?7 c))
+                 (js2-report-scan-error "msg.missing.octal.digits")
+               (while (and (<= ?0 c) (>= ?7 c))
+                 (js2-add-to-string c)
+                 (setq c (js2-get-char)))))
+            (t
+             (while (and (<= ?0 c) (<= c ?9))
+               ;; We permit 08 and 09 as decimal numbers, which
+               ;; makes our behavior a superset of the ECMA
+               ;; numeric grammar.  We might not always be so
+               ;; permissive, so we warn about it.
+               (when (and (eq base 'maybe-8) (>= c ?8))
+                 (js2-report-warning "msg.bad.octal.literal"
+                                     (if (eq c ?8) "8" "9"))
+                 (setq base 10))
+               (js2-add-to-string c)
+               (setq c (js2-get-char)))
+             (when (eq base 'maybe-8)
+               (setq base 8))))
+           (when (and (eq base 10) (memq c '(?. ?e ?E)))
+             (when (eq c ?.)
+               (cl-loop do
+                        (js2-add-to-string c)
+                        (setq c (js2-get-char))
+                        while (js2-digit-p c)))
+             (when (memq c '(?e ?E))
+               (js2-add-to-string c)
+               (setq c (js2-get-char))
+               (when (memq c '(?+ ?-))
+                 (js2-add-to-string c)
+                 (setq c (js2-get-char)))
+               (unless (js2-digit-p c)
+                 (js2-report-scan-error "msg.missing.exponent" t))
+               (cl-loop do
+                        (js2-add-to-string c)
+                        (setq c (js2-get-char))
+                        while (js2-digit-p c))))
+           (js2-unget-char)
+           (let ((str (js2-set-string-from-buffer token)))
+             (setf (js2-token-number token)
+                   (js2-string-to-number str base)))
+           (throw 'return js2-NUMBER))
+         ;; is it a string?
+         (when (or (memq c '(?\" ?\'))
+                   (and (>= js2-language-version 200)
+                        (= c ?`)))
+           (throw 'return
+                  (js2-get-string-or-template-token c token)))
+         (js2-ts-return token
+                        (cl-case c
+                          (?\;
+                           (throw 'return js2-SEMI))
+                          (?\[
+                           (throw 'return js2-LB))
+                          (?\]
+                           (throw 'return js2-RB))
+                          (?{
+                           (throw 'return js2-LC))
+                          (?}
+                           (throw 'return js2-RC))
+                          (?\(
+                           (throw 'return js2-LP))
+                          (?\)
+                           (throw 'return js2-RP))
+                          (?,
+                           (throw 'return js2-COMMA))
+                          (??
+                           (throw 'return js2-HOOK))
+                          (?:
+                           (if (js2-match-char ?:)
+                               js2-COLONCOLON
+                             (throw 'return js2-COLON)))
+                          (?.
+                           (if (js2-match-char ?.)
+                               (if (js2-match-char ?.)
+                                   js2-TRIPLEDOT js2-DOTDOT)
+                             (if (js2-match-char ?\()
+                                 js2-DOTQUERY
+                               (throw 'return js2-DOT))))
+                          (?|
+                           (if (js2-match-char ?|)
+                               (throw 'return js2-OR)
+                             (if (js2-match-char ?=)
+                                 js2-ASSIGN_BITOR
+                               (throw 'return js2-BITOR))))
+                          (?^
+                           (if (js2-match-char ?=)
+                               js2-ASSIGN_BITOR
+                             (throw 'return js2-BITXOR)))
+                          (?&
+                           (if (js2-match-char ?&)
+                               (throw 'return js2-AND)
+                             (if (js2-match-char ?=)
+                                 js2-ASSIGN_BITAND
+                               (throw 'return js2-BITAND))))
+                          (?=
+                           (if (js2-match-char ?=)
+                               (if (js2-match-char ?=)
+                                   js2-SHEQ
+                                 (throw 'return js2-EQ))
+                             (if (js2-match-char ?>)
+                                 (js2-ts-return token js2-ARROW)
+                               (throw 'return js2-ASSIGN))))
+                          (?!
+                           (if (js2-match-char ?=)
+                               (if (js2-match-char ?=)
+                                   js2-SHNE
+                                 js2-NE)
+                             (throw 'return js2-NOT)))
+                          (?<
+                           ;; NB:treat HTML begin-comment as comment-till-eol
+                           (when (js2-match-char ?!)
+                             (when (js2-match-char ?-)
+                               (when (js2-match-char ?-)
+                                 (js2-skip-line)
+                                 (setf (js2-token-comment-type (js2-current-token)) 'html)
+                                 (throw 'return js2-COMMENT)))
+                             (js2-unget-char))
+                           (if (js2-match-char ?<)
+                               (if (js2-match-char ?=)
+                                   js2-ASSIGN_LSH
+                                 js2-LSH)
+                             (if (js2-match-char ?=)
+                                 js2-LE
+                               (throw 'return js2-LT))))
+                          (?>
+                           (if (js2-match-char ?>)
+                               (if (js2-match-char ?>)
+                                   (if (js2-match-char ?=)
+                                       js2-ASSIGN_URSH
+                                     js2-URSH)
+                                 (if (js2-match-char ?=)
+                                     js2-ASSIGN_RSH
+                                   js2-RSH))
+                             (if (js2-match-char ?=)
+                                 js2-GE
+                               (throw 'return js2-GT))))
+                          (?*
+                           (if (js2-match-char ?=)
+                               js2-ASSIGN_MUL
+                             (throw 'return js2-MUL)))
+                          (?/
+                           ;; is it a // comment?
+                           (when (js2-match-char ?/)
+                             (setf (js2-token-beg token) (- js2-ts-cursor 2))
+                             (js2-skip-line)
+                             (setf (js2-token-comment-type token) 'line)
+                             ;; include newline so highlighting goes to end of window
+                             (cl-incf (js2-token-end token))
+                             (throw 'return js2-COMMENT))
+                           ;; is it a /* comment?
+                           (when (js2-match-char ?*)
+                             (setf look-for-slash nil
+                                   (js2-token-beg token) (- js2-ts-cursor 2)
+                                   (js2-token-comment-type token)
+                                   (if (js2-match-char ?*)
+                                       (progn
+                                         (setq look-for-slash t)
+                                         'jsdoc)
+                                     'block))
+                             (while t
+                               (setq c (js2-get-char))
+                               (cond
+                                ((eq c js2-EOF_CHAR)
+                                 (setf (js2-token-end token) (1- js2-ts-cursor))
+                                 (js2-report-error "msg.unterminated.comment")
+                                 (throw 'return js2-COMMENT))
+                                ((eq c ?*)
+                                 (setq look-for-slash t))
+                                ((eq c ?/)
+                                 (if look-for-slash
+                                     (js2-ts-return token js2-COMMENT)))
+                                (t
+                                 (setf look-for-slash nil
+                                       (js2-token-end token) js2-ts-cursor)))))
+                           (if (js2-match-char ?=)
+                               js2-ASSIGN_DIV
+                             (throw 'return js2-DIV)))
+                          (?#
+                           (when js2-skip-preprocessor-directives
+                             (js2-skip-line)
+                             (setf (js2-token-comment-type token) 'preprocessor
+                                   (js2-token-end token) js2-ts-cursor)
+                             (throw 'return js2-COMMENT))
+                           (throw 'return js2-ERROR))
+                          (?%
+                           (if (js2-match-char ?=)
+                               js2-ASSIGN_MOD
+                             (throw 'return js2-MOD)))
+                          (?~
+                           (throw 'return js2-BITNOT))
+                          (?+
+                           (if (js2-match-char ?=)
+                               js2-ASSIGN_ADD
+                             (if (js2-match-char ?+)
+                                 js2-INC
+                               (throw 'return js2-ADD))))
+                          (?-
+                           (cond
+                            ((js2-match-char ?=)
+                             (setq c js2-ASSIGN_SUB))
+                            ((js2-match-char ?-)
+                             (unless js2-ts-dirty-line
+                               ;; treat HTML end-comment after possible whitespace
+                               ;; after line start as comment-until-eol
+                               (when (js2-match-char ?>)
+                                 (js2-skip-line)
+                                 (setf (js2-token-comment-type (js2-current-token)) 'html)
+                                 (throw 'return js2-COMMENT)))
+                             (setq c js2-DEC))
+                            (t
+                             (setq c js2-SUB)))
+                           (setq js2-ts-dirty-line t)
+                           c)
+                          (otherwise
+                           (js2-report-scan-error "msg.illegal.character")))))))
+    (setf (js2-token-type token) tt)
+    token))
+
+(defun js2-get-string-or-template-token (quote-char token)
+  ;; We attempt to accumulate a string the fast way, by
+  ;; building it directly out of the reader.  But if there
+  ;; are any escaped characters in the string, we revert to
+  ;; building it out of a string buffer.
+  (let ((c (js2-get-char))
+        js2-ts-string-buffer
+        nc)
+    (catch 'break
+      (while (/= c quote-char)
+        (catch 'continue
+          (when (eq c js2-EOF_CHAR)
+            (js2-unget-char)
+            (js2-report-error "msg.unterminated.string.lit")
+            (throw 'break nil))
+          (when (and (eq c ?\n) (not (eq quote-char ?`)))
+            (js2-unget-char)
+            (js2-report-error "msg.unterminated.string.lit")
+            (throw 'break nil))
+          (when (eq c ?\\)
+            ;; We've hit an escaped character
+            (setq c (js2-get-char))
+            (cl-case c
+              (?b (setq c ?\b))
+              (?f (setq c ?\f))
+              (?n (setq c ?\n))
+              (?r (setq c ?\r))
+              (?t (setq c ?\t))
+              (?v (setq c ?\v))
+              (?u
+               (setq c1 (js2-read-unicode-escape))
+               (if js2-parse-ide-mode
+                   (if c1
+                       (progn
+                         ;; just copy the string in IDE-mode
+                         (js2-add-to-string ?\\)
+                         (js2-add-to-string ?u)
+                         (dotimes (_ 3)
+                           (js2-add-to-string (js2-get-char)))
+                         (setq c (js2-get-char))) ; added at end of loop
+                     ;; flag it as an invalid escape
+                     (js2-report-warning "msg.invalid.escape"
+                                         nil (- js2-ts-cursor 2) 6))
+                 ;; Get 4 hex digits; if the u escape is not
+                 ;; followed by 4 hex digits, use 'u' + the
+                 ;; literal character sequence that follows.
+                 (js2-add-to-string ?u)
+                 (setq escape-val 0)
+                 (dotimes (_ 4)
+                   (setq c (js2-get-char)
+                         escape-val (js2-x-digit-to-int c escape-val))
+                   (if (cl-minusp escape-val)
+                       (throw 'continue nil))
+                   (js2-add-to-string c))
+                 ;; prepare for replace of stored 'u' sequence by escape value
+                 (setq js2-ts-string-buffer (nthcdr 5 js2-ts-string-buffer)
+                       c escape-val)))
+              (?x
+               ;; Get 2 hex digits, defaulting to 'x'+literal
+               ;; sequence, as above.
+               (setq c (js2-get-char)
+                     escape-val (js2-x-digit-to-int c 0))
+               (if (cl-minusp escape-val)
+                   (progn
+                     (js2-add-to-string ?x)
+                     (throw 'continue nil))
+                 (setq c1 c
+                       c (js2-get-char)
+                       escape-val (js2-x-digit-to-int c escape-val))
+                 (if (cl-minusp escape-val)
+                     (progn
+                       (js2-add-to-string ?x)
+                       (js2-add-to-string c1)
+                       (throw 'continue nil))
+                   ;; got 2 hex digits
+                   (setq c escape-val))))
+              (?\n
+               ;; Remove line terminator after escape to follow
+               ;; SpiderMonkey and C/C++
+               (setq c (js2-get-char))
+               (throw 'continue nil))
+              (t
+               (when (and (<= ?0 c) (< c ?8))
+                 (setq val (- c ?0)
+                       c (js2-get-char))
+                 (when (and (<= ?0 c) (< c ?8))
+                   (setq val (- (+ (* 8 val) c) ?0)
+                         c (js2-get-char))
+                   (when (and (<= ?0 c)
+                              (< c ?8)
+                              (< val #o37))
+                     ;; c is 3rd char of octal sequence only
+                     ;; if the resulting val <= 0377
+                     (setq val (- (+ (* 8 val) c) ?0)
+                           c (js2-get-char))))
+                 (js2-unget-char)
+                 (setq c val)))))
+          (when (and (eq quote-char ?`) (eq c ?$))
+            (when (eq (setq nc (js2-get-char)) ?\{)
+              (throw 'break nil))
+            (js2-unget-char))
+          (js2-add-to-string c)
+          (setq c (js2-get-char)))))
+    (js2-set-string-from-buffer token)
+    (if (not (eq quote-char ?`))
+        js2-STRING
+      (if (and (eq c ?$) (eq nc ?\{))
+          js2-TEMPLATE_HEAD
+        js2-NO_SUBS_TEMPLATE))))
+
+(defun js2-read-regexp (start-tt)
+  "Called by parser when it gets / or /= in literal context."
+  (let (c err
+        in-class  ; inside a '[' .. ']' character-class
+        flags
+        (continue t)
+        (token (js2-new-token 0)))
+    (setq js2-ts-string-buffer nil)
+    (if (eq start-tt js2-ASSIGN_DIV)
+        ;; mis-scanned /=
+        (js2-add-to-string ?=)
+      (if (not (eq start-tt js2-DIV))
+          (error "failed assertion")))
+    (while (and (not err)
+                (or (/= (setq c (js2-get-char)) ?/)
+                    in-class))
+      (cond
+       ((or (= c ?\n)
+            (= c js2-EOF_CHAR))
+        (setf (js2-token-end token) (1- js2-ts-cursor)
+              err t
+              (js2-token-string token) (js2-collect-string js2-ts-string-buffer))
+        (js2-report-error "msg.unterminated.re.lit"))
+       (t (cond
+           ((= c ?\\)
+            (js2-add-to-string c)
+            (setq c (js2-get-char)))
+           ((= c ?\[)
+            (setq in-class t))
+           ((= c ?\])
+            (setq in-class nil)))
+          (js2-add-to-string c))))
+    (unless err
+      (while continue
+        (cond
+         ((js2-match-char ?g)
+          (push ?g flags))
+         ((js2-match-char ?i)
+          (push ?i flags))
+         ((js2-match-char ?m)
+          (push ?m flags))
+         ((and (js2-match-char ?u)
+               (>= js2-language-version 200))
+          (push ?u flags))
+         ((and (js2-match-char ?y)
+               (>= js2-language-version 200))
+          (push ?y flags))
+         (t
+          (setq continue nil))))
+      (if (js2-alpha-p (js2-peek-char))
+          (js2-report-scan-error "msg.invalid.re.flag" t
+                                 js2-ts-cursor 1))
+      (js2-set-string-from-buffer token))
+    (js2-collect-string flags)))
+
+(defun js2-get-first-xml-token ()
+  (setq js2-ts-xml-open-tags-count 0
+        js2-ts-is-xml-attribute nil
+        js2-ts-xml-is-tag-content nil)
+  (js2-unget-char)
+  (js2-get-next-xml-token))
+
+(defun js2-xml-discard-string (token)
+  "Throw away the string in progress and flag an XML parse error."
+  (setf js2-ts-string-buffer nil
+        (js2-token-string token) nil)
+  (js2-report-scan-error "msg.XML.bad.form" t))
+
+(defun js2-get-next-xml-token ()
+  (setq js2-ts-string-buffer nil)  ; for recording the XML
+  (let ((token (js2-new-token 0))
+        c result)
+    (setq result
+          (catch 'return
+            (while t
+              (setq c (js2-get-char))
+              (cond
+               ((= c js2-EOF_CHAR)
+                (throw 'return js2-ERROR))
+               (js2-ts-xml-is-tag-content
+                (cl-case c
+                  (?>
+                   (js2-add-to-string c)
+                   (setq js2-ts-xml-is-tag-content nil
+                         js2-ts-is-xml-attribute nil))
+                  (?/
+                   (js2-add-to-string c)
+                   (when (eq ?> (js2-peek-char))
+                     (setq c (js2-get-char))
+                     (js2-add-to-string c)
+                     (setq js2-ts-xml-is-tag-content nil)
+                     (cl-decf js2-ts-xml-open-tags-count)))
+                  (?{
+                   (js2-unget-char)
+                   (js2-set-string-from-buffer token)
+                   (throw 'return js2-XML))
+                  ((?\' ?\")
+                   (js2-add-to-string c)
+                   (unless (js2-read-quoted-string c token)
+                     (throw 'return js2-ERROR)))
+                  (?=
+                   (js2-add-to-string c)
+                   (setq js2-ts-is-xml-attribute t))
+                  ((? ?\t ?\r ?\n)
+                   (js2-add-to-string c))
+                  (t
+                   (js2-add-to-string c)
+                   (setq js2-ts-is-xml-attribute nil)))
+                (when (and (not js2-ts-xml-is-tag-content)
+                           (zerop js2-ts-xml-open-tags-count))
+                  (js2-set-string-from-buffer token)
+                  (throw 'return js2-XMLEND)))
+               (t
+                ;; else not tag content
+                (cl-case c
+                  (?<
+                   (js2-add-to-string c)
+                   (setq c (js2-peek-char))
+                   (cl-case c
+                     (?!
+                      (setq c (js2-get-char)) ;; skip !
+                      (js2-add-to-string c)
+                      (setq c (js2-peek-char))
+                      (cl-case c
+                        (?-
+                         (setq c (js2-get-char)) ;; skip -
+                         (js2-add-to-string c)
+                         (if (eq c ?-)
+                             (progn
+                               (js2-add-to-string c)
+                               (unless (js2-read-xml-comment token)
+                                 (throw 'return js2-ERROR)))
+                           (js2-xml-discard-string token)
+                           (throw 'return js2-ERROR)))
+                        (?\[
+                         (setq c (js2-get-char)) ;; skip [
+                         (js2-add-to-string c)
+                         (if (and (= (js2-get-char) ?C)
+                                  (= (js2-get-char) ?D)
+                                  (= (js2-get-char) ?A)
+                                  (= (js2-get-char) ?T)
+                                  (= (js2-get-char) ?A)
+                                  (= (js2-get-char) ?\[))
+                             (progn
+                               (js2-add-to-string ?C)
+                               (js2-add-to-string ?D)
+                               (js2-add-to-string ?A)
+                               (js2-add-to-string ?T)
+                               (js2-add-to-string ?A)
+                               (js2-add-to-string ?\[)
+                               (unless (js2-read-cdata token)
+                                 (throw 'return js2-ERROR)))
+                           (js2-xml-discard-string token)
+                           (throw 'return js2-ERROR)))
+                        (t
+                         (unless (js2-read-entity token)
+                           (throw 'return js2-ERROR))))
+                      ;; Allow bare CDATA section, e.g.:
+                      ;;   let xml = <![CDATA[ foo bar baz ]]>;
+                      (when (zerop js2-ts-xml-open-tags-count)
+                        (throw 'return js2-XMLEND)))
+                     (??
+                      (setq c (js2-get-char)) ;; skip ?
+                      (js2-add-to-string c)
+                      (unless (js2-read-PI token)
+                        (throw 'return js2-ERROR)))
+                     (?/
+                      ;; end tag
+                      (setq c (js2-get-char)) ;; skip /
+                      (js2-add-to-string c)
+                      (when (zerop js2-ts-xml-open-tags-count)
+                        (js2-xml-discard-string token)
+                        (throw 'return js2-ERROR))
+                      (setq js2-ts-xml-is-tag-content t)
+                      (cl-decf js2-ts-xml-open-tags-count))
+                     (t
+                      ;; start tag
+                      (setq js2-ts-xml-is-tag-content t)
+                      (cl-incf js2-ts-xml-open-tags-count))))
+                  (?{
+                   (js2-unget-char)
+                   (js2-set-string-from-buffer token)
+                   (throw 'return js2-XML))
+                  (t
+                   (js2-add-to-string c))))))))
+    (setf (js2-token-end token) js2-ts-cursor)
+    (setf (js2-token-type token) result)
+    result))
+
+(defun js2-read-quoted-string (quote token)
+  (let (c)
+    (catch 'return
+      (while (/= (setq c (js2-get-char)) js2-EOF_CHAR)
+        (js2-add-to-string c)
+        (if (eq c quote)
+            (throw 'return t)))
+      (js2-xml-discard-string token)  ;; throw away string in progress
+      nil)))
+
+(defun js2-read-xml-comment (token)
+  (let ((c (js2-get-char)))
+    (catch 'return
+      (while (/= c js2-EOF_CHAR)
+        (catch 'continue
+          (js2-add-to-string c)
+          (when (and (eq c ?-) (eq ?- (js2-peek-char)))
+            (setq c (js2-get-char))
+            (js2-add-to-string c)
+            (if (eq (js2-peek-char) ?>)
+                (progn
+                  (setq c (js2-get-char)) ;; skip >
+                  (js2-add-to-string c)
+                  (throw 'return t))
+              (throw 'continue nil)))
+          (setq c (js2-get-char))))
+      (js2-xml-discard-string token)
+      nil)))
+
+(defun js2-read-cdata (token)
+  (let ((c (js2-get-char)))
+    (catch 'return
+      (while (/= c js2-EOF_CHAR)
+        (catch 'continue
+          (js2-add-to-string c)
+          (when (and (eq c ?\]) (eq (js2-peek-char) ?\]))
+            (setq c (js2-get-char))
+            (js2-add-to-string c)
+            (if (eq (js2-peek-char) ?>)
+                (progn
+                  (setq c (js2-get-char)) ;; Skip >
+                  (js2-add-to-string c)
+                  (throw 'return t))
+              (throw 'continue nil)))
+          (setq c (js2-get-char))))
+      (js2-xml-discard-string token)
+      nil)))
+
+(defun js2-read-entity (token)
+  (let ((decl-tags 1)
+        c)
+    (catch 'return
+      (while (/= js2-EOF_CHAR (setq c (js2-get-char)))
+        (js2-add-to-string c)
+        (cl-case c
+          (?<
+           (cl-incf decl-tags))
+          (?>
+           (cl-decf decl-tags)
+           (if (zerop decl-tags)
+               (throw 'return t)))))
+      (js2-xml-discard-string token)
+      nil)))
+
+(defun js2-read-PI (token)
+  "Scan an XML processing instruction."
+  (let (c)
+    (catch 'return
+      (while (/= js2-EOF_CHAR (setq c (js2-get-char)))
+        (js2-add-to-string c)
+        (when (and (eq c ??) (eq (js2-peek-char) ?>))
+          (setq c (js2-get-char))  ;; Skip >
+          (js2-add-to-string c)
+          (throw 'return t)))
+      (js2-xml-discard-string token)
+      nil)))
+
+;;; Highlighting
+
+(defun js2-set-face (beg end face &optional record)
+  "Fontify a region.  If RECORD is non-nil, record for later."
+  (when (cl-plusp js2-highlight-level)
+    (setq beg (min (point-max) beg)
+          beg (max (point-min) beg)
+          end (min (point-max) end)
+          end (max (point-min) end))
+    (if record
+        (push (list beg end face) js2-mode-fontifications)
+      (put-text-property beg end 'font-lock-face face))))
+
+(defsubst js2-clear-face (beg end)
+  (remove-text-properties beg end '(font-lock-face nil
+                                    help-echo nil
+                                    point-entered nil
+                                    c-in-sws nil)))
+
+(defconst js2-ecma-global-props
+  (concat "^"
+          (regexp-opt
+           '("Infinity" "NaN" "undefined" "arguments") t)
+          "$")
+  "Value properties of the Ecma-262 Global Object.
+Shown at or above `js2-highlight-level' 2.")
+
+;; might want to add the name "arguments" to this list?
+(defconst js2-ecma-object-props
+  (concat "^"
+          (regexp-opt
+           '("prototype" "__proto__" "__parent__") t)
+          "$")
+  "Value properties of the Ecma-262 Object constructor.
+Shown at or above `js2-highlight-level' 2.")
+
+(defconst js2-ecma-global-funcs
+  (concat
+   "^"
+   (regexp-opt
+    '("decodeURI" "decodeURIComponent" "encodeURI" "encodeURIComponent"
+      "eval" "isFinite" "isNaN" "parseFloat" "parseInt") t)
+   "$")
+  "Function properties of the Ecma-262 Global object.
+Shown at or above `js2-highlight-level' 2.")
+
+(defconst js2-ecma-number-props
+  (concat "^"
+          (regexp-opt '("MAX_VALUE" "MIN_VALUE" "NaN"
+                        "NEGATIVE_INFINITY"
+                        "POSITIVE_INFINITY") t)
+          "$")
+  "Properties of the Ecma-262 Number constructor.
+Shown at or above `js2-highlight-level' 2.")
+
+(defconst js2-ecma-date-props "^\\(parse\\|UTC\\)$"
+  "Properties of the Ecma-262 Date constructor.
+Shown at or above `js2-highlight-level' 2.")
+
+(defconst js2-ecma-math-props
+  (concat "^"
+          (regexp-opt
+           '("E" "LN10" "LN2" "LOG2E" "LOG10E" "PI" "SQRT1_2" "SQRT2")
+           t)
+          "$")
+  "Properties of the Ecma-262 Math object.
+Shown at or above `js2-highlight-level' 2.")
+
+(defconst js2-ecma-math-funcs
+  (concat "^"
+          (regexp-opt
+           '("abs" "acos" "asin" "atan" "atan2" "ceil" "cos" "exp" "floor"
+             "log" "max" "min" "pow" "random" "round" "sin" "sqrt" "tan") t)
+          "$")
+  "Function properties of the Ecma-262 Math object.
+Shown at or above `js2-highlight-level' 2.")
+
+(defconst js2-ecma-function-props
+  (concat
+   "^"
+   (regexp-opt
+    '(;; properties of the Object prototype object
+      "hasOwnProperty" "isPrototypeOf" "propertyIsEnumerable"
+      "toLocaleString" "toString" "valueOf"
+      ;; properties of the Function prototype object
+      "apply" "call"
+      ;; properties of the Array prototype object
+      "concat" "join" "pop" "push" "reverse" "shift" "slice" "sort"
+      "splice" "unshift"
+      ;; properties of the String prototype object
+      "charAt" "charCodeAt" "fromCharCode" "indexOf" "lastIndexOf"
+      "localeCompare" "match" "replace" "search" "split" "substring"
+      "toLocaleLowerCase" "toLocaleUpperCase" "toLowerCase"
+      "toUpperCase"
+      ;; properties of the Number prototype object
+      "toExponential" "toFixed" "toPrecision"
+      ;; properties of the Date prototype object
+      "getDate" "getDay" "getFullYear" "getHours" "getMilliseconds"
+      "getMinutes" "getMonth" "getSeconds" "getTime"
+      "getTimezoneOffset" "getUTCDate" "getUTCDay" "getUTCFullYear"
+      "getUTCHours" "getUTCMilliseconds" "getUTCMinutes" "getUTCMonth"
+      "getUTCSeconds" "setDate" "setFullYear" "setHours"
+      "setMilliseconds" "setMinutes" "setMonth" "setSeconds" "setTime"
+      "setUTCDate" "setUTCFullYear" "setUTCHours" "setUTCMilliseconds"
+      "setUTCMinutes" "setUTCMonth" "setUTCSeconds" "toDateString"
+      "toLocaleDateString" "toLocaleString" "toLocaleTimeString"
+      "toTimeString" "toUTCString"
+      ;; properties of the RegExp prototype object
+      "exec" "test"
+      ;; properties of the JSON prototype object
+      "parse" "stringify"
+      ;; SpiderMonkey/Rhino extensions, versions 1.5+
+      "toSource" "__defineGetter__" "__defineSetter__"
+      "__lookupGetter__" "__lookupSetter__" "__noSuchMethod__"
+      "every" "filter" "forEach" "lastIndexOf" "map" "some")
+    t)
+   "$")
+  "Built-in functions defined by Ecma-262 and SpiderMonkey extensions.
+Shown at or above `js2-highlight-level' 3.")
+
+(defun js2-parse-highlight-prop-get (parent target prop call-p)
+  (let ((target-name (and target
+                          (js2-name-node-p target)
+                          (js2-name-node-name target)))
+        (prop-name (if prop (js2-name-node-name prop)))
+        (level2 (>= js2-highlight-level 2))
+        (level3 (>= js2-highlight-level 3)))
+    (when level2
+      (let ((face
+             (if call-p
+                 (cond
+                  ((and target prop)
+                   (cond
+                    ((and level3 (string-match js2-ecma-function-props prop-name))
+                     'font-lock-builtin-face)
+                    ((and target-name prop)
+                     (cond
+                      ((string= target-name "Date")
+                       (if (string-match js2-ecma-date-props prop-name)
+                           'font-lock-builtin-face))
+                      ((string= target-name "Math")
+                       (if (string-match js2-ecma-math-funcs prop-name)
+                           'font-lock-builtin-face))))))
+                  (prop
+                   (if (string-match js2-ecma-global-funcs prop-name)
+                       'font-lock-builtin-face)))
+               (cond
+                ((and target prop)
+                 (cond
+                  ((string= target-name "Number")
+                   (if (string-match js2-ecma-number-props prop-name)
+                       'font-lock-constant-face))
+                  ((string= target-name "Math")
+                   (if (string-match js2-ecma-math-props prop-name)
+                       'font-lock-constant-face))))
+                (prop
+                 (if (string-match js2-ecma-object-props prop-name)
+                     'font-lock-constant-face))))))
+        (when face
+          (let ((pos (+ (js2-node-pos parent)  ; absolute
+                        (js2-node-pos prop)))) ; relative
+            (js2-set-face pos
+                          (+ pos (js2-node-len prop))
+                          face 'record)))))))
+
+(defun js2-parse-highlight-member-expr-node (node)
+  "Perform syntax highlighting of EcmaScript built-in properties.
+The variable `js2-highlight-level' governs this highighting."
+  (let (face target prop name pos end parent call-p callee)
+    (cond
+     ;; case 1:  simple name, e.g. foo
+     ((js2-name-node-p node)
+      (setq name (js2-name-node-name node))
+      ;; possible for name to be nil in rare cases - saw it when
+      ;; running js2-mode on an elisp buffer.  Might as well try to
+      ;; make it so js2-mode never barfs.
+      (when name
+        (setq face (if (string-match js2-ecma-global-props name)
+                       'font-lock-constant-face))
+        (when face
+          (setq pos (js2-node-pos node)
+                end (+ pos (js2-node-len node)))
+          (js2-set-face pos end face 'record))))
+     ;; case 2:  property access or function call
+     ((or (js2-prop-get-node-p node)
+          ;; highlight function call if expr is a prop-get node
+          ;; or a plain name (i.e. unqualified function call)
+          (and (setq call-p (js2-call-node-p node))
+               (setq callee (js2-call-node-target node)) ; separate setq!
+               (or (js2-prop-get-node-p callee)
+                   (js2-name-node-p callee))))
+      (setq parent node
+            node (if call-p callee node))
+      (if (and call-p (js2-name-node-p callee))
+          (setq prop callee)
+        (setq target (js2-prop-get-node-left node)
+              prop (js2-prop-get-node-right node)))
+      (cond
+       ((js2-name-node-p prop)
+        ;; case 2(a&c):  simple or complex target, simple name, e.g. x[y].bar
+        (js2-parse-highlight-prop-get parent target prop call-p))
+       ((js2-name-node-p target)
+        ;; case 2b:  simple target, complex name, e.g. foo.x[y]
+        (js2-parse-highlight-prop-get parent target nil call-p)))))))
+
+(defun js2-parse-highlight-member-expr-fn-name (expr)
+  "Highlight the `baz' in function foo.bar.baz(args) {...}.
+This is experimental Rhino syntax.  EXPR is the foo.bar.baz member expr.
+We currently only handle the case where the last component is a prop-get
+of a simple name.  Called before EXPR has a parent node."
+  (let (pos
+        (name (and (js2-prop-get-node-p expr)
+                   (js2-prop-get-node-right expr))))
+    (when (js2-name-node-p name)
+      (js2-set-face (setq pos (+ (js2-node-pos expr)  ; parent is absolute
+                                 (js2-node-pos name)))
+                    (+ pos (js2-node-len name))
+                    'font-lock-function-name-face
+                    'record))))
+
+;; source:  http://jsdoc.sourceforge.net/
+;; Note - this syntax is for Google's enhanced jsdoc parser that
+;; allows type specifications, and needs work before entering the wild.
+
+(defconst js2-jsdoc-param-tag-regexp
+  (concat "^\\s-*\\*+\\s-*\\(@"
+          "\\(?:param\\|argument\\)"
+          "\\)"
+          "\\s-*\\({[^}]+}\\)?"         ; optional type
+          "\\s-*\\[?\\([[:alnum:]_$\.]+\\)?\\]?"  ; name
+          "\\>")
+  "Matches jsdoc tags with optional type and optional param name.")
+
+(defconst js2-jsdoc-typed-tag-regexp
+  (concat "^\\s-*\\*+\\s-*\\(@\\(?:"
+          (regexp-opt
+           '("enum"
+             "extends"
+             "field"
+             "id"
+             "implements"
+             "lends"
+             "mods"
+             "requires"
+             "return"
+             "returns"
+             "throw"
+             "throws"))
+          "\\)\\)\\s-*\\({[^}]+}\\)?")
+  "Matches jsdoc tags with optional type.")
+
+(defconst js2-jsdoc-arg-tag-regexp
+  (concat "^\\s-*\\*+\\s-*\\(@\\(?:"
+          (regexp-opt
+           '("alias"
+             "augments"
+             "borrows"
+             "bug"
+             "base"
+             "config"
+             "default"
+             "define"
+             "exception"
+             "function"
+             "member"
+             "memberOf"
+             "name"
+             "namespace"
+             "property"
+             "since"
+             "suppress"
+             "this"
+             "throws"
+             "type"
+             "version"))
+          "\\)\\)\\s-+\\([^ \t]+\\)")
+  "Matches jsdoc tags with a single argument.")
+
+(defconst js2-jsdoc-empty-tag-regexp
+  (concat "^\\s-*\\*+\\s-*\\(@\\(?:"
+          (regexp-opt
+           '("addon"
+             "author"
+             "class"
+             "const"
+             "constant"
+             "constructor"
+             "constructs"
+             "deprecated"
+             "desc"
+             "description"
+             "event"
+             "example"
+             "exec"
+             "export"
+             "fileoverview"
+             "final"
+             "function"
+             "hidden"
+             "ignore"
+             "implicitCast"
+             "inheritDoc"
+             "inner"
+             "interface"
+             "license"
+             "noalias"
+             "noshadow"
+             "notypecheck"
+             "override"
+             "owner"
+             "preserve"
+             "preserveTry"
+             "private"
+             "protected"
+             "public"
+             "static"
+             "supported"
+             ))
+          "\\)\\)\\s-*")
+  "Matches empty jsdoc tags.")
+
+(defconst js2-jsdoc-link-tag-regexp
+  "{\\(@\\(?:link\\|code\\)\\)\\s-+\\([^#}\n]+\\)\\(#.+\\)?}"
+  "Matches a jsdoc link or code tag.")
+
+(defconst js2-jsdoc-see-tag-regexp
+  "^\\s-*\\*+\\s-*\\(@see\\)\\s-+\\([^#}\n]+\\)\\(#.+\\)?"
+  "Matches a jsdoc @see tag.")
+
+(defconst js2-jsdoc-html-tag-regexp
+  "\\(</?\\)\\([[:alpha:]]+\\)\\s-*\\(/?>\\)"
+  "Matches a simple (no attributes) html start- or end-tag.")
+
+(defun js2-jsdoc-highlight-helper ()
+  (js2-set-face (match-beginning 1)
+                (match-end 1)
+                'js2-jsdoc-tag)
+  (if (match-beginning 2)
+      (if (save-excursion
+            (goto-char (match-beginning 2))
+            (= (char-after) ?{))
+          (js2-set-face (1+ (match-beginning 2))
+                        (1- (match-end 2))
+                        'js2-jsdoc-type)
+        (js2-set-face (match-beginning 2)
+                      (match-end 2)
+                      'js2-jsdoc-value)))
+  (if (match-beginning 3)
+      (js2-set-face (match-beginning 3)
+                    (match-end 3)
+                    'js2-jsdoc-value)))
+
+(defun js2-highlight-jsdoc (ast)
+  "Highlight doc comment tags."
+  (let ((comments (js2-ast-root-comments ast))
+        beg end)
+    (save-excursion
+      (dolist (node comments)
+        (when (eq (js2-comment-node-format node) 'jsdoc)
+          (setq beg (js2-node-abs-pos node)
+                end (+ beg (js2-node-len node)))
+          (save-restriction
+            (narrow-to-region beg end)
+            (dolist (re (list js2-jsdoc-param-tag-regexp
+                              js2-jsdoc-typed-tag-regexp
+                              js2-jsdoc-arg-tag-regexp
+                              js2-jsdoc-link-tag-regexp
+                              js2-jsdoc-see-tag-regexp
+                              js2-jsdoc-empty-tag-regexp))
+              (goto-char beg)
+              (while (re-search-forward re nil t)
+                (js2-jsdoc-highlight-helper)))
+            ;; simple highlighting for html tags
+            (goto-char beg)
+            (while (re-search-forward js2-jsdoc-html-tag-regexp nil t)
+              (js2-set-face (match-beginning 1)
+                            (match-end 1)
+                            'js2-jsdoc-html-tag-delimiter)
+              (js2-set-face (match-beginning 2)
+                            (match-end 2)
+                            'js2-jsdoc-html-tag-name)
+              (js2-set-face (match-beginning 3)
+                            (match-end 3)
+                            'js2-jsdoc-html-tag-delimiter))))))))
+
+(defun js2-highlight-assign-targets (_node left right)
+  "Highlight function properties and external variables."
+  (let (leftpos name)
+    ;; highlight vars and props assigned function values
+    (when (or (js2-function-node-p right)
+              (js2-class-node-p right))
+      (cond
+       ;; var foo = function() {...}
+       ((js2-name-node-p left)
+        (setq name left))
+       ;; foo.bar.baz = function() {...}
+       ((and (js2-prop-get-node-p left)
+             (js2-name-node-p (js2-prop-get-node-right left)))
+        (setq name (js2-prop-get-node-right left))))
+      (when name
+        (js2-set-face (setq leftpos (js2-node-abs-pos name))
+                      (+ leftpos (js2-node-len name))
+                      'font-lock-function-name-face
+                      'record)))))
+
+(defun js2-record-name-node (node)
+  "Saves NODE to `js2-recorded-identifiers' to check for undeclared variables
+later. NODE must be a name node."
+  (let ((leftpos (js2-node-abs-pos node)))
+    (push (list node js2-current-scope
+                leftpos
+                (+ leftpos (js2-node-len node)))
+          js2-recorded-identifiers)))
+
+(defun js2-highlight-undeclared-vars ()
+  "After entire parse is finished, look for undeclared variable references.
+We have to wait until entire buffer is parsed, since JavaScript permits var
+decls to occur after they're used.
+
+If any undeclared var name is in `js2-externs' or `js2-additional-externs',
+it is considered declared."
+  (let (name)
+    (dolist (entry js2-recorded-identifiers)
+      (cl-destructuring-bind (name-node scope pos end) entry
+        (setq name (js2-name-node-name name-node))
+        (unless (or (member name js2-global-externs)
+                    (member name js2-default-externs)
+                    (member name js2-additional-externs)
+                    (js2-get-defining-scope scope name))
+          (js2-report-warning "msg.undeclared.variable" name pos (- end pos)
+                              'js2-external-variable))))
+    (setq js2-recorded-identifiers nil)))
+
+(defun js2-set-default-externs ()
+  "Set the value of `js2-default-externs' based on the various
+`js2-include-?-externs' variables."
+  (setq js2-default-externs
+        (append js2-ecma-262-externs
+                (if js2-include-browser-externs js2-browser-externs)
+                (if (and js2-include-browser-externs
+                         (>= js2-language-version 200)) js2-harmony-externs)
+                (if js2-include-rhino-externs js2-rhino-externs)
+                (if js2-include-node-externs js2-node-externs)
+                (if (or js2-include-browser-externs js2-include-node-externs)
+                    js2-typed-array-externs))))
+
+(defun js2-apply-jslint-globals ()
+  (setq js2-additional-externs
+        (nconc (js2-get-jslint-globals)
+               js2-additional-externs)))
+
+(defun js2-get-jslint-globals ()
+  (cl-loop for node in (js2-ast-root-comments js2-mode-ast)
+           when (and (eq 'block (js2-comment-node-format node))
+                     (save-excursion
+                       (goto-char (js2-node-abs-pos node))
+                       (looking-at "/\\*global ")))
+           append (js2-get-jslint-globals-in
+                   (match-end 0)
+                   (js2-node-abs-end node))))
+
+(defun js2-get-jslint-globals-in (beg end)
+  (let (res)
+    (save-excursion
+      (goto-char beg)
+      (while (re-search-forward js2-mode-identifier-re end t)
+        (let ((match (match-string 0)))
+          (unless (member match '("true" "false"))
+            (push match res)))))
+    (nreverse res)))
+
+;;; IMenu support
+
+;; We currently only support imenu, but eventually should support speedbar and
+;; possibly other browsing mechanisms.
+
+;; The basic strategy is to identify function assignment targets of the form
+;; `foo.bar.baz', convert them to (list fn foo bar baz <position>), and push the
+;; list into `js2-imenu-recorder'.  The lists are merged into a trie-like tree
+;; for imenu after parsing is finished.
+
+;; A `foo.bar.baz' assignment target may be expressed in many ways in
+;; JavaScript, and the general problem is undecidable.  However, several forms
+;; are readily recognizable at parse-time; the forms we attempt to recognize
+;; include:
+
+;;  function foo()  -- function declaration
+;;  foo = function()  -- function expression assigned to variable
+;;  foo.bar.baz = function()  -- function expr assigned to nested property-get
+;;  foo = {bar: function()}  -- fun prop in object literal assigned to var
+;;  foo = {bar: {baz: function()}} -- inside nested object literal
+;;  foo.bar = {baz: function()}} -- obj lit assigned to nested prop get
+;;  a.b = {c: {d: function()}} -- nested obj lit assigned to nested prop get
+;;  foo = {get bar() {...}}  -- getter/setter in obj literal
+;;  function foo() {function bar() {...}}  -- nested function
+;;  foo['a'] = function()  -- fun expr assigned to deterministic element-get
+
+;; This list boils down to a few forms that can be combined recursively.
+;; Top-level named function declarations include both the left-hand (name)
+;; and the right-hand (function value) expressions needed to produce an imenu
+;; entry.  The other "right-hand" forms we need to look for are:
+;;  - functions declared as props/getters/setters in object literals
+;;  - nested named function declarations
+;; The "left-hand" expressions that functions can be assigned to include:
+;;  - local/global variables
+;;  - nested property-get expressions like a.b.c.d
+;;  - element gets like foo[10] or foo['bar'] where the index
+;;    expression can be trivially converted to a property name.  They
+;;    effectively then become property gets.
+
+;; All the different definition types are canonicalized into the form
+;; foo.bar.baz = position-of-function-keyword
+
+;; We need to build a trie-like structure for imenu.  As an example,
+;; consider the following JavaScript code:
+
+;; a = function() {...}  // function at position 5
+;; b = function() {...}  // function at position 25
+;; foo = function() {...} // function at position 100
+;; foo.bar = function() {...} // function at position 200
+;; foo.bar.baz = function() {...} // function at position 300
+;; foo.bar.zab = function() {...} // function at position 400
+
+;; During parsing we accumulate an entry for each definition in
+;; the variable `js2-imenu-recorder', like so:
+
+;; '((fn a 5)
+;;   (fn b 25)
+;;   (fn foo 100)
+;;   (fn foo bar 200)
+;;   (fn foo bar baz 300)
+;;   (fn foo bar zab 400))
+
+;; Where 'fn' is the respective function node.
+;; After parsing these entries are merged into this alist-trie:
+
+;; '((a . 1)
+;;   (b . 2)
+;;   (foo (<definition> . 3)
+;;        (bar (<definition> . 6)
+;;             (baz . 100)
+;;             (zab . 200))))
+
+;; Note the wacky need for a <definition> name.  The token can be anything
+;; that isn't a valid JavaScript identifier, because you might make foo
+;; a function and then start setting properties on it that are also functions.
+
+(defun js2-prop-node-name (node)
+  "Return the name of a node that may be a property-get/property-name.
+If NODE is not a valid name-node, string-node or integral number-node,
+returns nil.  Otherwise returns the string name/value of the node."
+  (cond
+   ((js2-name-node-p node)
+    (js2-name-node-name node))
+   ((js2-string-node-p node)
+    (js2-string-node-value node))
+   ((and (js2-number-node-p node)
+         (string-match "^[0-9]+$" (js2-number-node-value node)))
+    (js2-number-node-value node))
+   ((eq (js2-node-type node) js2-THIS)
+    "this")
+   ((eq (js2-node-type node) js2-SUPER)
+    "super")))
+
+(defun js2-node-qname-component (node)
+  "Return the name of this node, if it contributes to a qname.
+Returns nil if the node doesn't contribute."
+  (copy-sequence
+   (or (js2-prop-node-name node)
+       (if (and (js2-function-node-p node)
+                (js2-function-node-name node))
+           (js2-name-node-name (js2-function-node-name node))))))
+
+(defun js2-record-imenu-entry (fn-node qname pos)
+  "Add an entry to `js2-imenu-recorder'.
+FN-NODE should be the current item's function node.
+
+Associate FN-NODE with its QNAME for later lookup.
+This is used in postprocessing the chain list.  For each chain, we find
+the parent function, look up its qname, then prepend a copy of it to the chain."
+  (push (cons fn-node (append qname (list pos))) js2-imenu-recorder)
+  (unless js2-imenu-function-map
+    (setq js2-imenu-function-map (make-hash-table :test 'eq)))
+  (puthash fn-node qname js2-imenu-function-map))
+
+(defun js2-record-imenu-functions (node &optional var)
+  "Record function definitions for imenu.
+NODE is a function node or an object literal.
+VAR, if non-nil, is the expression that NODE is being assigned to.
+When passed arguments of wrong type, does nothing."
+  (when js2-parse-ide-mode
+    (let ((fun-p (js2-function-node-p node))
+          qname fname-node)
+      (cond
+       ;; non-anonymous function declaration?
+       ((and fun-p
+             (not var)
+             (setq fname-node (js2-function-node-name node)))
+        (js2-record-imenu-entry node (list fname-node) (js2-node-pos node)))
+       ;; for remaining forms, compute left-side tree branch first
+       ((and var (setq qname (js2-compute-nested-prop-get var)))
+        (cond
+         ;; foo.bar.baz = function
+         (fun-p
+          (js2-record-imenu-entry node qname (js2-node-pos node)))
+         ;; foo.bar.baz = object-literal
+         ;; look for nested functions:  {a: {b: function() {...} }}
+         ((js2-object-node-p node)
+          ;; Node position here is still absolute, since the parser
+          ;; passes the assignment target and value expressions
+          ;; to us before they are added as children of the assignment node.
+          (js2-record-object-literal node qname (js2-node-pos node)))))))))
+
+(defun js2-compute-nested-prop-get (node)
+  "If NODE is of form foo.bar, foo['bar'], or any nested combination, return
+component nodes as a list.  Otherwise return nil.  Element-gets are treated
+as property-gets if the index expression is a string, or a positive integer."
+  (let (left right head)
+    (cond
+     ((or (js2-name-node-p node)
+          (js2-this-or-super-node-p node))
+      (list node))
+     ;; foo.bar.baz is parenthesized as (foo.bar).baz => right operand is a leaf
+     ((js2-prop-get-node-p node)        ; foo.bar
+      (setq left (js2-prop-get-node-left node)
+            right (js2-prop-get-node-right node))
+      (if (setq head (js2-compute-nested-prop-get left))
+          (nconc head (list right))))
+     ((js2-elem-get-node-p node)        ; foo['bar'] or foo[101]
+      (setq left (js2-elem-get-node-target node)
+            right (js2-elem-get-node-element node))
+      (if (or (js2-string-node-p right)      ; ['bar']
+              (and (js2-number-node-p right) ; [10]
+                   (string-match "^[0-9]+$"
+                                 (js2-number-node-value right))))
+          (if (setq head (js2-compute-nested-prop-get left))
+              (nconc head (list right))))))))
+
+(defun js2-record-object-literal (node qname pos)
+  "Recursively process an object literal looking for functions.
+NODE is an object literal that is the right-hand child of an assignment
+expression.  QNAME is a list of nodes representing the assignment target,
+e.g. for foo.bar.baz = {...}, QNAME is (foo-node bar-node baz-node).
+POS is the absolute position of the node.
+We do a depth-first traversal of NODE.  For any functions we find,
+we append the property name to QNAME, then call `js2-record-imenu-entry'."
+  (let (right)
+    (dolist (e (js2-object-node-elems node))  ; e is a `js2-object-prop-node'
+      (let ((left (js2-infix-node-left e))
+            ;; Element positions are relative to the parent position.
+            (pos (+ pos (js2-node-pos e))))
+        (cond
+         ;; foo: function() {...}
+         ((js2-function-node-p (setq right (js2-infix-node-right e)))
+          (when (js2-prop-node-name left)
+            ;; As a policy decision, we record the position of the property,
+            ;; not the position of the `function' keyword, since the property
+            ;; is effectively the name of the function.
+            (js2-record-imenu-entry right (append qname (list left)) pos)))
+         ;; foo: {object-literal} -- add foo to qname, offset position, and recurse
+         ((js2-object-node-p right)
+          (js2-record-object-literal right
+                                     (append qname (list (js2-infix-node-left e)))
+                                     (+ pos (js2-node-pos right)))))))))
+
+(defun js2-node-top-level-decl-p (node)
+  "Return t if NODE's name is defined in the top-level scope.
+Also returns t if NODE's name is not defined in any scope, since it implies
+that it's an external variable, which must also be in the top-level scope."
+  (let* ((name (js2-prop-node-name node))
+         (this-scope (js2-node-get-enclosing-scope node))
+         defining-scope)
+    (cond
+     ((js2-this-or-super-node-p node)
+      nil)
+     ((null this-scope)
+      t)
+     ((setq defining-scope (js2-get-defining-scope this-scope name))
+      (js2-ast-root-p defining-scope))
+     (t t))))
+
+(defun js2-wrapper-function-p (node)
+  "Return t if NODE is a function expression that's immediately invoked.
+NODE must be `js2-function-node'."
+  (let ((parent (js2-node-parent node)))
+    (or
+     ;; function(){...}();
+     (and (js2-call-node-p parent)
+          (eq node (js2-call-node-target parent)))
+     (and (js2-paren-node-p parent)
+          ;; (function(){...})();
+          (or (js2-call-node-p (setq parent (js2-node-parent parent)))
+              ;; (function(){...}).call(this);
+              (and (js2-prop-get-node-p parent)
+                   (member (js2-name-node-name (js2-prop-get-node-right parent))
+                           '("call" "apply"))
+                   (js2-call-node-p (js2-node-parent parent))))))))
+
+(defun js2-browse-postprocess-chains ()
+  "Modify function-declaration name chains after parsing finishes.
+Some of the information is only available after the parse tree is complete.
+For instance, processing a nested scope requires a parent function node."
+  (let (result fn parent-qname p elem)
+    (dolist (entry js2-imenu-recorder)
+      ;; function node goes first
+      (cl-destructuring-bind (current-fn &rest (&whole chain head &rest)) entry
+        ;; Examine head's defining scope:
+        ;; Pre-processed chain, or top-level/external, keep as-is.
+        (if (or (stringp head) (js2-node-top-level-decl-p head))
+            (push chain result)
+          (when (js2-this-or-super-node-p head)
+            (setq chain (cdr chain))) ; discard this-node
+          (when (setq fn (js2-node-parent-script-or-fn current-fn))
+            (setq parent-qname (gethash fn js2-imenu-function-map 'not-found))
+            (when (eq parent-qname 'not-found)
+              ;; anonymous function expressions are not recorded
+              ;; during the parse, so we need to handle this case here
+              (setq parent-qname
+                    (if (js2-wrapper-function-p fn)
+                        (let ((grandparent (js2-node-parent-script-or-fn fn)))
+                          (if (js2-ast-root-p grandparent)
+                              nil
+                            (gethash grandparent js2-imenu-function-map 'skip)))
+                      'skip))
+              (puthash fn parent-qname js2-imenu-function-map))
+            (if (eq parent-qname 'skip)
+                ;; We don't show it, let's record that fact.
+                (remhash current-fn js2-imenu-function-map)
+              ;; Prepend parent fn qname to this chain.
+              (let ((qname (append parent-qname chain)))
+                (puthash current-fn (butlast qname) js2-imenu-function-map)
+                (push qname result)))))))
+    ;; Collect chains obtained by third-party code.
+    (let (js2-imenu-recorder)
+      (run-hooks 'js2-build-imenu-callbacks)
+      (dolist (entry js2-imenu-recorder)
+        (push (cdr entry) result)))
+    ;; Finally replace each node in each chain with its name.
+    (dolist (chain result)
+      (setq p chain)
+      (while p
+        (if (js2-node-p (setq elem (car p)))
+            (setcar p (js2-node-qname-component elem)))
+        (setq p (cdr p))))
+    result))
+
+;; Merge name chains into a trie-like tree structure of nested lists.
+;; To simplify construction of the trie, we first build it out using the rule
+;; that the trie consists of lists of pairs.  Each pair is a 2-element array:
+;; [key, num-or-list].  The second element can be a number; if so, this key
+;; is a leaf-node with only one value.  (I.e. there is only one declaration
+;; associated with the key at this level.)  Otherwise the second element is
+;; a list of pairs, with the rule applied recursively.  This symmetry permits
+;; a simple recursive formulation.
+;;
+;; js2-mode is building the data structure for imenu.  The imenu documentation
+;; claims that it's the structure above, but in practice it wants the children
+;; at the same list level as the key for that level, which is how I've drawn
+;; the "Expected final result" above.  We'll postprocess the trie to remove the
+;; list wrapper around the children at each level.
+;;
+;; A completed nested imenu-alist entry looks like this:
+;;       '(("foo"
+;;          ("<definition>" . 7)
+;;          ("bar"
+;;           ("a" . 40)
+;;           ("b" . 60))))
+;;
+;; In particular, the documentation for `imenu--index-alist' says that
+;; a nested sub-alist element looks like (INDEX-NAME SUB-ALIST).
+;; The sub-alist entries immediately follow INDEX-NAME, the head of the list.
+
+(defun js2-treeify (lst)
+  "Convert (a b c d) to (a ((b ((c d)))))."
+  (if (null (cddr lst))  ; list length <= 2
+      lst
+    (list (car lst) (list (js2-treeify (cdr lst))))))
+
+(defun js2-build-alist-trie (chains trie)
+  "Merge declaration name chains into a trie-like alist structure for imenu.
+CHAINS is the qname chain list produced during parsing. TRIE is a
+list of elements built up so far."
+  (let (head tail pos branch kids)
+    (dolist (chain chains)
+      (setq head (car chain)
+            tail (cdr chain)
+            pos (if (numberp (car tail)) (car tail))
+            branch (js2-find-if (lambda (n)
+                                  (string= (car n) head))
+                                trie)
+            kids (cl-second branch))
+      (cond
+       ;; case 1:  this key isn't in the trie yet
+       ((null branch)
+        (if trie
+            (setcdr (last trie) (list (js2-treeify chain)))
+          (setq trie (list (js2-treeify chain)))))
+       ;; case 2:  key is present with a single number entry:  replace w/ list
+       ;;  ("a1" 10)  +  ("a1" 20) => ("a1" (("<definition>" 10)
+       ;;                                    ("<definition>" 20)))
+       ((numberp kids)
+        (setcar (cdr branch)
+                (list (list "<definition-1>" kids)
+                      (if pos
+                          (list "<definition-2>" pos)
+                        (js2-treeify tail)))))
+       ;; case 3:  key is there (with kids), and we're a number entry
+       (pos
+        (setcdr (last kids)
+                (list
+                 (list (format "<definition-%d>"
+                               (1+ (cl-loop for kid in kids
+                                            count (eq ?< (aref (car kid) 0)))))
+                       pos))))
+       ;; case 4:  key is there with kids, need to merge in our chain
+       (t
+        (js2-build-alist-trie (list tail) kids))))
+    trie))
+
+(defun js2-flatten-trie (trie)
+  "Convert TRIE to imenu-format.
+Recurses through nodes, and for each one whose second element is a list,
+appends the list's flattened elements to the current element.  Also
+changes the tails into conses.  For instance, this pre-flattened trie
+
+'(a ((b 20)
+     (c ((d 30)
+         (e 40)))))
+
+becomes
+
+'(a (b . 20)
+    (c (d . 30)
+       (e . 40)))
+
+Note that the root of the trie has no key, just a list of chains.
+This is also true for the value of any key with multiple children,
+e.g. key 'c' in the example above."
+  (cond
+   ((listp (car trie))
+    (mapcar #'js2-flatten-trie trie))
+   (t
+    (if (numberp (cl-second trie))
+        (cons (car trie) (cl-second trie))
+      ;; else pop list and append its kids
+      (apply #'append (list (car trie)) (js2-flatten-trie (cdr trie)))))))
+
+(defun js2-build-imenu-index ()
+  "Turn `js2-imenu-recorder' into an imenu data structure."
+  (when (eq js2-imenu-recorder 'empty)
+    (setq js2-imenu-recorder nil))
+  (let* ((chains (js2-browse-postprocess-chains))
+         (result (js2-build-alist-trie chains nil)))
+    (js2-flatten-trie result)))
+
+(defun js2-test-print-chains (chains)
+  "Print a list of qname chains.
+Each element of CHAINS is a list of the form (NODE [NODE *] pos);
+i.e. one or more nodes, and an integer position as the list tail."
+  (mapconcat (lambda (chain)
+               (concat "("
+                       (mapconcat (lambda (elem)
+                                    (if (js2-node-p elem)
+                                        (or (js2-node-qname-component elem)
+                                            "nil")
+                                      (number-to-string elem)))
+                                  chain
+                                  " ")
+                       ")"))
+             chains
+             "\n"))
+
+;;; Parser
+
+(defconst js2-version "1.8.5"
+  "Version of JavaScript supported.")
+
+(defun js2-record-face (face &optional token)
+  "Record a style run of FACE for TOKEN or the current token."
+  (unless token (setq token (js2-current-token)))
+  (js2-set-face (js2-token-beg token) (js2-token-end token) face 'record))
+
+(defsubst js2-node-end (n)
+  "Computes the absolute end of node N.
+Use with caution!  Assumes `js2-node-pos' is -absolute-, which
+is only true until the node is added to its parent; i.e., while parsing."
+  (+ (js2-node-pos n)
+     (js2-node-len n)))
+
+(defun js2-record-comment (token)
+  "Record a comment in `js2-scanned-comments'."
+  (let ((ct (js2-token-comment-type token))
+        (beg (js2-token-beg token))
+        (end (js2-token-end token)))
+    (push (make-js2-comment-node :len (- end beg)
+                                 :format ct)
+          js2-scanned-comments)
+    (when js2-parse-ide-mode
+      (js2-record-face (if (eq ct 'jsdoc)
+                           'font-lock-doc-face
+                         'font-lock-comment-face)
+                       token)
+      (when (memq ct '(html preprocessor))
+        ;; Tell cc-engine the bounds of the comment.
+        (js2-record-text-property beg (1- end) 'c-in-sws t)))))
+
+(defun js2-peek-token ()
+  "Return the next token type without consuming it.
+If `js2-ti-lookahead' is positive, return the type of next token
+from `js2-ti-tokens'.  Otherwise, call `js2-get-token'."
+  (if (not (zerop js2-ti-lookahead))
+      (js2-token-type
+       (aref js2-ti-tokens (mod (1+ js2-ti-tokens-cursor) js2-ti-ntokens)))
+    (let ((tt (js2-get-token-internal nil)))
+      (js2-unget-token)
+      tt)))
+
+(defalias 'js2-next-token 'js2-get-token)
+
+(defun js2-match-token (match &optional dont-unget)
+  "Get next token and return t if it matches MATCH, a bytecode.
+Returns nil and consumes nothing if MATCH is not the next token."
+  (if (/= (js2-get-token) match)
+      (ignore (unless dont-unget (js2-unget-token)))
+    t))
+
+(defun js2-match-contextual-kwd (name)
+  "Consume and return t if next token is `js2-NAME', and its
+string is NAME.  Returns nil and keeps current token otherwise."
+  (if (or (/= (js2-get-token) js2-NAME)
+          (not (string= (js2-current-token-string) name)))
+      (progn
+        (js2-unget-token)
+        nil)
+    (js2-record-face 'font-lock-keyword-face)
+    t))
+
+(defun js2-get-prop-name-token ()
+  (js2-get-token (and (>= js2-language-version 170) 'KEYWORD_IS_NAME)))
+
+(defun js2-match-prop-name ()
+  "Consume token and return t if next token is a valid property name.
+If `js2-language-version' is >= 180, a keyword or reserved word
+is considered valid name as well."
+  (if (eq js2-NAME (js2-get-prop-name-token))
+      t
+    (js2-unget-token)
+    nil))
+
+(defun js2-must-match-prop-name (msg-id &optional pos len)
+  (if (js2-match-prop-name)
+      t
+    (js2-report-error msg-id nil pos len)
+    nil))
+
+(defun js2-peek-token-or-eol ()
+  "Return js2-EOL if the next token immediately follows a newline.
+Else returns the next token.  Used in situations where we don't
+consider certain token types valid if they are preceded by a newline.
+One example is the postfix ++ or -- operator, which has to be on the
+same line as its operand."
+  (let ((tt (js2-get-token))
+        (follows-eol (js2-token-follows-eol-p (js2-current-token))))
+    (js2-unget-token)
+    (if follows-eol
+        js2-EOL
+      tt)))
+
+(defun js2-must-match (token msg-id &optional pos len)
+  "Match next token to token code TOKEN, or record a syntax error.
+MSG-ID is the error message to report if the match fails.
+Returns t on match, nil if no match."
+  (if (js2-match-token token t)
+      t
+    (js2-report-error msg-id nil pos len)
+    (js2-unget-token)
+    nil))
+
+(defun js2-must-match-name (msg-id)
+  (if (js2-match-token js2-NAME t)
+      t
+    (if (eq (js2-current-token-type) js2-RESERVED)
+        (js2-report-error "msg.reserved.id" (js2-current-token-string))
+      (js2-report-error msg-id)
+      (js2-unget-token))
+    nil))
+
+(defsubst js2-inside-function ()
+  (cl-plusp js2-nesting-of-function))
+
+(defun js2-set-requires-activation ()
+  (if (js2-function-node-p js2-current-script-or-fn)
+      (setf (js2-function-node-needs-activation js2-current-script-or-fn) t)))
+
+(defun js2-check-activation-name (name _token)
+  (when (js2-inside-function)
+    ;; skip language-version 1.2 check from Rhino
+    (if (or (string= "arguments" name)
+            (and js2-compiler-activation-names  ; only used in codegen
+                 (gethash name js2-compiler-activation-names)))
+        (js2-set-requires-activation))))
+
+(defun js2-set-is-generator ()
+  (let ((fn-node js2-current-script-or-fn))
+    (when (and (js2-function-node-p fn-node)
+               (not (js2-function-node-generator-type fn-node)))
+      (setf (js2-function-node-generator-type js2-current-script-or-fn) 'LEGACY))))
+
+(defun js2-must-have-xml ()
+  (unless js2-compiler-xml-available
+    (js2-report-error "msg.XML.not.available")))
+
+(defun js2-push-scope (scope)
+  "Push SCOPE, a `js2-scope', onto the lexical scope chain."
+  (cl-assert (js2-scope-p scope))
+  (cl-assert (null (js2-scope-parent-scope scope)))
+  (cl-assert (not (eq js2-current-scope scope)))
+  (setf (js2-scope-parent-scope scope) js2-current-scope
+        js2-current-scope scope))
+
+(defsubst js2-pop-scope ()
+  (setq js2-current-scope
+        (js2-scope-parent-scope js2-current-scope)))
+
+(defun js2-enter-loop (loop-node)
+  (push loop-node js2-loop-set)
+  (push loop-node js2-loop-and-switch-set)
+  (js2-push-scope loop-node)
+  ;; Tell the current labeled statement (if any) its statement,
+  ;; and set the jump target of the first label to the loop.
+  ;; These are used in `js2-parse-continue' to verify that the
+  ;; continue target is an actual labeled loop.  (And for codegen.)
+  (when js2-labeled-stmt
+    (setf (js2-labeled-stmt-node-stmt js2-labeled-stmt) loop-node
+          (js2-label-node-loop (car (js2-labeled-stmt-node-labels
+                                     js2-labeled-stmt))) loop-node)))
+
+(defun js2-exit-loop ()
+  (pop js2-loop-set)
+  (pop js2-loop-and-switch-set)
+  (js2-pop-scope))
+
+(defsubst js2-enter-switch (switch-node)
+  (push switch-node js2-loop-and-switch-set))
+
+(defsubst js2-exit-switch ()
+  (pop js2-loop-and-switch-set))
+
+(defun js2-parse (&optional buf cb)
+  "Tell the js2 parser to parse a region of JavaScript.
+
+BUF is a buffer or buffer name containing the code to parse.
+Call `narrow-to-region' first to parse only part of the buffer.
+
+The returned AST root node is given some additional properties:
+  `node-count' - total number of nodes in the AST
+  `buffer' - BUF.  The buffer it refers to may change or be killed,
+             so the value is not necessarily reliable.
+
+An optional callback CB can be specified to report parsing
+progress.  If `(functionp CB)' returns t, it will be called with
+the current line number once before parsing begins, then again
+each time the lexer reaches a new line number.
+
+CB can also be a list of the form `(symbol cb ...)' to specify
+multiple callbacks with different criteria.  Each symbol is a
+criterion keyword, and the following element is the callback to
+call
+
+  :line  - called whenever the line number changes
+  :token - called for each new token consumed
+
+The list of criteria could be extended to include entering or
+leaving a statement, an expression, or a function definition."
+  (if (and cb (not (functionp cb)))
+      (error "criteria callbacks not yet implemented"))
+  (let ((inhibit-point-motion-hooks t)
+        (js2-compiler-xml-available (>= js2-language-version 160))
+        ;; This is a recursive-descent parser, so give it a big stack.
+        (max-lisp-eval-depth (max max-lisp-eval-depth 3000))
+        (max-specpdl-size (max max-specpdl-size 3000))
+        (case-fold-search nil)
+        ast)
+    (with-current-buffer (or buf (current-buffer))
+      (setq js2-scanned-comments nil
+            js2-parsed-errors nil
+            js2-parsed-warnings nil
+            js2-imenu-recorder nil
+            js2-imenu-function-map nil
+            js2-label-set nil)
+      (js2-init-scanner)
+      (setq ast (js2-do-parse))
+      (unless js2-ts-hit-eof
+        (js2-report-error "msg.got.syntax.errors" (length js2-parsed-errors)))
+      (setf (js2-ast-root-errors ast) js2-parsed-errors
+            (js2-ast-root-warnings ast) js2-parsed-warnings)
+      ;; if we didn't find any declarations, put a dummy in this list so we
+      ;; don't end up re-parsing the buffer in `js2-mode-create-imenu-index'
+      (unless js2-imenu-recorder
+        (setq js2-imenu-recorder 'empty))
+      (run-hooks 'js2-parse-finished-hook)
+      ast)))
+
+;; Corresponds to Rhino's Parser.parse() method.
+(defun js2-do-parse ()
+  "Parse current buffer starting from current point.
+Scanner should be initialized."
+  (let ((pos js2-ts-cursor)
+        (end js2-ts-cursor)  ; in case file is empty
+        root n tt)
+    ;; initialize buffer-local parsing vars
+    (setf root (make-js2-ast-root :buffer (buffer-name) :pos pos)
+          js2-current-script-or-fn root
+          js2-current-scope root
+          js2-nesting-of-function 0
+          js2-labeled-stmt nil
+          js2-recorded-identifiers nil)  ; for js2-highlight
+    (while (/= (setq tt (js2-get-token)) js2-EOF)
+      (if (= tt js2-FUNCTION)
+          (progn
+            (setq n (if js2-called-by-compile-function
+                        (js2-parse-function-expr)
+                      (js2-parse-function-stmt))))
+        ;; not a function - parse a statement
+        (js2-unget-token)
+        (setq n (js2-parse-statement)))
+      ;; add function or statement to script
+      (setq end (js2-node-end n))
+      (js2-block-node-push root n))
+    ;; add comments to root in lexical order
+    (when js2-scanned-comments
+      ;; if we find a comment beyond end of normal kids, use its end
+      (setq end (max end (js2-node-end (cl-first js2-scanned-comments))))
+      (dolist (comment js2-scanned-comments)
+        (push comment (js2-ast-root-comments root))
+        (js2-node-add-children root comment)))
+    (setf (js2-node-len root) (- end pos))
+    (setq js2-mode-ast root)  ; Make sure this is available for callbacks.
+    ;; Give extensions a chance to muck with things before highlighting starts.
+    (let ((js2-additional-externs js2-additional-externs))
+      (save-excursion
+        (run-hooks 'js2-post-parse-callbacks))
+      (js2-highlight-undeclared-vars))
+    root))
+
+(defun js2-parse-function-closure-body (fn-node)
+  "Parse a JavaScript 1.8 function closure body."
+  (let ((js2-nesting-of-function (1+ js2-nesting-of-function)))
+    (if js2-ts-hit-eof
+        (js2-report-error "msg.no.brace.body" nil
+                          (js2-node-pos fn-node)
+                          (- js2-ts-cursor (js2-node-pos fn-node)))
+      (js2-node-add-children fn-node
+                             (setf (js2-function-node-body fn-node)
+                                   (js2-parse-expr t))))))
+
+(defun js2-parse-function-body (fn-node)
+  (js2-must-match js2-LC "msg.no.brace.body"
+                  (js2-node-pos fn-node)
+                  (- js2-ts-cursor (js2-node-pos fn-node)))
+  (let ((pos (js2-current-token-beg))         ; LC position
+        (pn (make-js2-block-node))  ; starts at LC position
+        tt
+        end)
+    (cl-incf js2-nesting-of-function)
+    (unwind-protect
+        (while (not (or (= (setq tt (js2-peek-token)) js2-ERROR)
+                        (= tt js2-EOF)
+                        (= tt js2-RC)))
+          (js2-block-node-push pn (if (/= tt js2-FUNCTION)
+                                      (js2-parse-statement)
+                                    (js2-get-token)
+                                    (js2-parse-function-stmt))))
+      (cl-decf js2-nesting-of-function))
+    (setq end (js2-current-token-end))  ; assume no curly and leave at current token
+    (if (js2-must-match js2-RC "msg.no.brace.after.body" pos)
+        (setq end (js2-current-token-end)))
+    (setf (js2-node-pos pn) pos
+          (js2-node-len pn) (- end pos))
+    (setf (js2-function-node-body fn-node) pn)
+    (js2-node-add-children fn-node pn)
+    pn))
+
+(defun js2-define-destruct-symbols (node decl-type face &optional ignore-not-in-block)
+  "Declare and fontify destructuring parameters inside NODE.
+NODE is either `js2-array-node', `js2-object-node', or `js2-name-node'."
+  (cond
+   ((js2-name-node-p node)
+    (let (leftpos)
+      (js2-define-symbol decl-type (js2-name-node-name node)
+                         node ignore-not-in-block)
+      (when face
+        (js2-set-face (setq leftpos (js2-node-abs-pos node))
+                      (+ leftpos (js2-node-len node))
+                      face 'record))))
+   ((js2-object-node-p node)
+    (dolist (elem (js2-object-node-elems node))
+      (js2-define-destruct-symbols
+       ;; In abbreviated destructuring {a, b}, right == left.
+       (js2-object-prop-node-right elem)
+       decl-type face ignore-not-in-block)))
+   ((js2-array-node-p node)
+    (dolist (elem (js2-array-node-elems node))
+      (when elem
+        (js2-define-destruct-symbols elem decl-type face ignore-not-in-block))))
+   (t (js2-report-error "msg.no.parm" nil (js2-node-abs-pos node)
+                        (js2-node-len node)))))
+
+(defun js2-parse-function-params (function-type fn-node pos)
+  (if (js2-match-token js2-RP)
+      (setf (js2-function-node-rp fn-node) (- (js2-current-token-beg) pos))
+    (let ((paren-free-arrow (and (eq function-type 'FUNCTION_ARROW)
+                                 (eq (js2-current-token-type) js2-NAME)))
+          params param default-found rest-param-at)
+      (when paren-free-arrow
+        (js2-unget-token))
+      (cl-loop for tt = (js2-peek-token)
+               do
+               (cond
+                ;; destructuring param
+                ((and (not paren-free-arrow)
+                      (or (= tt js2-LB) (= tt js2-LC)))
+                 (js2-get-token)
+                 (when default-found
+                   (js2-report-error "msg.no.default.after.default.param"))
+                 (setq param (js2-parse-destruct-primary-expr))
+                 (js2-define-destruct-symbols param
+                                              js2-LP
+                                              'js2-function-param)
+                 (push param params))
+                ;; variable name
+                (t
+                 (when (and (>= js2-language-version 200)
+                            (not paren-free-arrow)
+                            (js2-match-token js2-TRIPLEDOT)
+                            (not rest-param-at))
+                   ;; to report errors if there are more parameters
+                   (setq rest-param-at (length params)))
+                 (js2-must-match-name "msg.no.parm")
+                 (js2-record-face 'js2-function-param)
+                 (setq param (js2-create-name-node))
+                 (js2-define-symbol js2-LP (js2-current-token-string) param)
+                 ;; default parameter value
+                 (when (or (and default-found
+                                (not rest-param-at)
+                                (js2-must-match js2-ASSIGN
+                                                "msg.no.default.after.default.param"
+                                                (js2-node-pos param)
+                                                (js2-node-len param)))
+                           (and (>= js2-language-version 200)
+                                (js2-match-token js2-ASSIGN)))
+                   (cl-assert (not paren-free-arrow))
+                   (let* ((pos (js2-node-pos param))
+                          (tt (js2-current-token-type))
+                          (op-pos (- (js2-current-token-beg) pos))
+                          (left param)
+                          (right (js2-parse-assign-expr))
+                          (len (- (js2-node-end right) pos)))
+                     (setq param (make-js2-assign-node
+                                  :type tt :pos pos :len len :op-pos op-pos
+                                  :left left :right right)
+                           default-found t)
+                     (js2-node-add-children param left right)))
+                 (push param params)))
+               (when (and rest-param-at (> (length params) (1+ rest-param-at)))
+                 (js2-report-error "msg.param.after.rest" nil
+                                   (js2-node-pos param) (js2-node-len param)))
+               while
+               (js2-match-token js2-COMMA))
+      (when (and (not paren-free-arrow)
+                 (js2-must-match js2-RP "msg.no.paren.after.parms"))
+        (setf (js2-function-node-rp fn-node) (- (js2-current-token-beg) pos)))
+      (when rest-param-at
+        (setf (js2-function-node-rest-p fn-node) t))
+      (dolist (p params)
+        (js2-node-add-children fn-node p)
+        (push p (js2-function-node-params fn-node))))))
+
+(defun js2-check-inconsistent-return-warning (fn-node name)
+  "Possibly show inconsistent-return warning.
+Last token scanned is the close-curly for the function body."
+  (when (and js2-mode-show-strict-warnings
+             js2-strict-inconsistent-return-warning
+             (not (js2-has-consistent-return-usage
+                   (js2-function-node-body fn-node))))
+    ;; Have it extend from close-curly to bol or beginning of block.
+    (let ((pos (save-excursion
+                 (goto-char (js2-current-token-end))
+                 (max (js2-node-abs-pos (js2-function-node-body fn-node))
+                      (point-at-bol))))
+          (end (js2-current-token-end)))
+      (if (cl-plusp (js2-name-node-length name))
+          (js2-add-strict-warning "msg.no.return.value"
+                                  (js2-name-node-name name) pos end)
+        (js2-add-strict-warning "msg.anon.no.return.value" nil pos end)))))
+
+(defun js2-parse-function-stmt ()
+  (let ((pos (js2-current-token-beg))
+        (star-p (js2-match-token js2-MUL)))
+    (js2-must-match-name "msg.unnamed.function.stmt")
+    (let ((name (js2-create-name-node t))
+          pn member-expr)
+      (cond
+       ((js2-match-token js2-LP)
+        (js2-parse-function 'FUNCTION_STATEMENT pos star-p name))
+       (js2-allow-member-expr-as-function-name
+        (setq member-expr (js2-parse-member-expr-tail nil name))
+        (js2-parse-highlight-member-expr-fn-name member-expr)
+        (js2-must-match js2-LP "msg.no.paren.parms")
+        (setf pn (js2-parse-function 'FUNCTION_STATEMENT pos star-p)
+              (js2-function-node-member-expr pn) member-expr)
+        pn)
+       (t
+        (js2-report-error "msg.no.paren.parms")
+        (make-js2-error-node))))))
+
+(defun js2-parse-function-expr ()
+  (let ((pos (js2-current-token-beg))
+        (star-p (js2-match-token js2-MUL))
+        name)
+    (when (js2-match-token js2-NAME)
+      (setq name (js2-create-name-node t)))
+    (js2-must-match js2-LP "msg.no.paren.parms")
+    (js2-parse-function 'FUNCTION_EXPRESSION pos star-p name)))
+
+(defun js2-parse-function (function-type pos star-p &optional name)
+  "Function parser.  FUNCTION-TYPE is a symbol, POS is the
+beginning of the first token (function keyword, unless it's an
+arrow function), NAME is js2-name-node."
+  (let (fn-node lp)
+    (if (= (js2-current-token-type) js2-LP) ; eventually matched LP?
+        (setq lp (js2-current-token-beg)))
+    (setf fn-node (make-js2-function-node :pos pos
+                                          :name name
+                                          :form function-type
+                                          :lp (if lp (- lp pos))
+                                          :generator-type (and star-p 'STAR)))
+    (when name
+      (js2-set-face (js2-node-pos name) (js2-node-end name)
+                    'font-lock-function-name-face 'record)
+      (when (and (eq function-type 'FUNCTION_STATEMENT)
+                 (cl-plusp (js2-name-node-length name)))
+        ;; Function statements define a symbol in the enclosing scope
+        (js2-define-symbol js2-FUNCTION (js2-name-node-name name) fn-node)))
+    (if (or (js2-inside-function) (cl-plusp js2-nesting-of-with))
+        ;; 1. Nested functions are not affected by the dynamic scope flag
+        ;;    as dynamic scope is already a parent of their scope.
+        ;; 2. Functions defined under the with statement also immune to
+        ;;    this setup, in which case dynamic scope is ignored in favor
+        ;;    of the with object.
+        (setf (js2-function-node-ignore-dynamic fn-node) t))
+    ;; dynamically bind all the per-function variables
+    (let ((js2-current-script-or-fn fn-node)
+          (js2-current-scope fn-node)
+          (js2-nesting-of-with 0)
+          (js2-end-flags 0)
+          js2-label-set
+          js2-loop-set
+          js2-loop-and-switch-set)
+      (js2-parse-function-params function-type fn-node pos)
+      (when (eq function-type 'FUNCTION_ARROW)
+        (js2-must-match js2-ARROW "msg.bad.arrow.args"))
+      (if (and (>= js2-language-version 180)
+               (/= (js2-peek-token) js2-LC))
+          (js2-parse-function-closure-body fn-node)
+        (js2-parse-function-body fn-node))
+      (js2-check-inconsistent-return-warning fn-node name)
+
+      (when name
+        (js2-node-add-children fn-node name)
+        ;; Function expressions define a name only in the body of the
+        ;; function, and only if not hidden by a parameter name
+        (when (and (eq function-type 'FUNCTION_EXPRESSION)
+                   (null (js2-scope-get-symbol js2-current-scope
+                                               (js2-name-node-name name))))
+          (js2-define-symbol js2-FUNCTION
+                             (js2-name-node-name name)
+                             fn-node))
+        (when (eq function-type 'FUNCTION_STATEMENT)
+          (js2-record-imenu-functions fn-node))))
+
+    (setf (js2-node-len fn-node) (- js2-ts-cursor pos))
+    ;; Rhino doesn't do this, but we need it for finding undeclared vars.
+    ;; We wait until after parsing the function to set its parent scope,
+    ;; since `js2-define-symbol' needs the defining-scope check to stop
+    ;; at the function boundary when checking for redeclarations.
+    (setf (js2-scope-parent-scope fn-node) js2-current-scope)
+    fn-node))
+
+(defun js2-parse-statements (&optional parent)
+  "Parse a statement list.  Last token consumed must be js2-LC.
+
+PARENT can be a `js2-block-node', in which case the statements are
+appended to PARENT.  Otherwise a new `js2-block-node' is created
+and returned.
+
+This function does not match the closing js2-RC: the caller
+matches the RC so it can provide a suitable error message if not
+matched.  This means it's up to the caller to set the length of
+the node to include the closing RC.  The node start pos is set to
+the absolute buffer start position, and the caller should fix it
+up to be relative to the parent node.  All children of this block
+node are given relative start positions and correct lengths."
+  (let ((pn (or parent (make-js2-block-node)))
+        tt)
+    (while (and (> (setq tt (js2-peek-token)) js2-EOF)
+                (/= tt js2-RC))
+      (js2-block-node-push pn (js2-parse-statement)))
+    pn))
+
+(defun js2-parse-statement ()
+  (let (pn beg end)
+    ;; coarse-grained user-interrupt check - needs work
+    (and js2-parse-interruptable-p
+         (zerop (% (cl-incf js2-parse-stmt-count)
+                   js2-statements-per-pause))
+         (input-pending-p)
+         (throw 'interrupted t))
+    (setq pn (js2-statement-helper))
+    ;; no-side-effects warning check
+    (unless (js2-node-has-side-effects pn)
+      (setq end (js2-node-end pn))
+      (save-excursion
+        (goto-char end)
+        (setq beg (max (js2-node-pos pn) (point-at-bol))))
+      (js2-add-strict-warning "msg.no.side.effects" nil beg end))
+    pn))
+
+;; These correspond to the switch cases in Parser.statementHelper
+(defconst js2-parsers
+  (let ((parsers (make-vector js2-num-tokens
+                                #'js2-parse-expr-stmt)))
+    (aset parsers js2-BREAK     #'js2-parse-break)
+    (aset parsers js2-CLASS     #'js2-parse-class-stmt)
+    (aset parsers js2-CONST     #'js2-parse-const-var)
+    (aset parsers js2-CONTINUE  #'js2-parse-continue)
+    (aset parsers js2-DEBUGGER  #'js2-parse-debugger)
+    (aset parsers js2-DEFAULT   #'js2-parse-default-xml-namespace)
+    (aset parsers js2-DO        #'js2-parse-do)
+    (aset parsers js2-EXPORT    #'js2-parse-export)
+    (aset parsers js2-FOR       #'js2-parse-for)
+    (aset parsers js2-FUNCTION  #'js2-parse-function-stmt)
+    (aset parsers js2-IF        #'js2-parse-if)
+    (aset parsers js2-IMPORT    #'js2-parse-import)
+    (aset parsers js2-LC        #'js2-parse-block)
+    (aset parsers js2-LET       #'js2-parse-let-stmt)
+    (aset parsers js2-NAME      #'js2-parse-name-or-label)
+    (aset parsers js2-RETURN    #'js2-parse-ret-yield)
+    (aset parsers js2-SEMI      #'js2-parse-semi)
+    (aset parsers js2-SWITCH    #'js2-parse-switch)
+    (aset parsers js2-THROW     #'js2-parse-throw)
+    (aset parsers js2-TRY       #'js2-parse-try)
+    (aset parsers js2-VAR       #'js2-parse-const-var)
+    (aset parsers js2-WHILE     #'js2-parse-while)
+    (aset parsers js2-WITH      #'js2-parse-with)
+    (aset parsers js2-YIELD     #'js2-parse-ret-yield)
+    parsers)
+  "A vector mapping token types to parser functions.")
+
+(defun js2-parse-warn-missing-semi (beg end)
+  (and js2-mode-show-strict-warnings
+       js2-strict-missing-semi-warning
+       (js2-add-strict-warning
+        "msg.missing.semi" nil
+        ;; back up to beginning of statement or line
+        (max beg (save-excursion
+                   (goto-char end)
+                   (point-at-bol)))
+        end)))
+
+(defconst js2-no-semi-insertion
+  (list js2-IF
+        js2-SWITCH
+        js2-WHILE
+        js2-DO
+        js2-FOR
+        js2-TRY
+        js2-WITH
+        js2-LC
+        js2-ERROR
+        js2-SEMI
+        js2-CLASS
+        js2-FUNCTION)
+  "List of tokens that don't do automatic semicolon insertion.")
+
+(defconst js2-autoinsert-semi-and-warn
+  (list js2-ERROR js2-EOF js2-RC))
+
+(defun js2-statement-helper ()
+  (let* ((tt (js2-get-token))
+         (first-tt tt)
+         (parser (if (= tt js2-ERROR)
+                     #'js2-parse-semi
+                   (aref js2-parsers tt)))
+         pn)
+    ;; If the statement is set, then it's been told its label by now.
+    (and js2-labeled-stmt
+         (js2-labeled-stmt-node-stmt js2-labeled-stmt)
+         (setq js2-labeled-stmt nil))
+    (setq pn (funcall parser))
+    ;; Don't do auto semi insertion for certain statement types.
+    (unless (or (memq first-tt js2-no-semi-insertion)
+                (js2-labeled-stmt-node-p pn))
+      (js2-auto-insert-semicolon pn))
+    pn))
+
+(defun js2-auto-insert-semicolon (pn)
+  (let* ((tt (js2-get-token))
+         (pos (js2-node-pos pn)))
+      (cond
+       ((= tt js2-SEMI)
+        ;; extend the node bounds to include the semicolon.
+        (setf (js2-node-len pn) (- (js2-current-token-end) pos)))
+       ((memq tt js2-autoinsert-semi-and-warn)
+        (js2-unget-token) ; Not ';', do not consume.
+        ;; Autoinsert ;
+        (js2-parse-warn-missing-semi pos (js2-node-end pn)))
+       (t
+        (if (not (js2-token-follows-eol-p (js2-current-token)))
+            ;; Report error if no EOL or autoinsert ';' otherwise
+            (js2-report-error "msg.no.semi.stmt")
+          (js2-parse-warn-missing-semi pos (js2-node-end pn)))
+        (js2-unget-token) ; Not ';', do not consume.
+        ))))
+
+(defun js2-parse-condition ()
+  "Parse a parenthesized boolean expression, e.g. in an if- or while-stmt.
+The parens are discarded and the expression node is returned.
+The `pos' field of the return value is set to an absolute position
+that must be fixed up by the caller.
+Return value is a list (EXPR LP RP), with absolute paren positions."
+  (let (pn lp rp)
+    (if (js2-must-match js2-LP "msg.no.paren.cond")
+        (setq lp (js2-current-token-beg)))
+    (setq pn (js2-parse-expr))
+    (if (js2-must-match js2-RP "msg.no.paren.after.cond")
+        (setq rp (js2-current-token-beg)))
+    ;; Report strict warning on code like "if (a = 7) ..."
+    (if (and js2-strict-cond-assign-warning
+             (js2-assign-node-p pn))
+        (js2-add-strict-warning "msg.equal.as.assign" nil
+                                (js2-node-pos pn)
+                                (+ (js2-node-pos pn)
+                                   (js2-node-len pn))))
+    (list pn lp rp)))
+
+(defun js2-parse-if ()
+  "Parser for if-statement.  Last matched token must be js2-IF."
+  (let ((pos (js2-current-token-beg))
+        cond if-true if-false else-pos end pn)
+    (setq cond (js2-parse-condition)
+          if-true (js2-parse-statement)
+          if-false (if (js2-match-token js2-ELSE)
+                       (progn
+                         (setq else-pos (- (js2-current-token-beg) pos))
+                         (js2-parse-statement)))
+          end (js2-node-end (or if-false if-true))
+          pn (make-js2-if-node :pos pos
+                               :len (- end pos)
+                               :condition (car cond)
+                               :then-part if-true
+                               :else-part if-false
+                               :else-pos else-pos
+                               :lp (js2-relpos (cl-second cond) pos)
+                               :rp (js2-relpos (cl-third cond) pos)))
+    (js2-node-add-children pn (car cond) if-true if-false)
+    pn))
+
+(defun js2-parse-import ()
+  "Parse import statement. The current token must be js2-IMPORT."
+  (unless (js2-ast-root-p js2-current-scope)
+    (js2-report-error "msg.mod.import.decl.at.top.level"))
+  (let ((beg (js2-current-token-beg)))
+    (cond ((js2-match-token js2-STRING)
+           (make-js2-import-node
+            :pos beg
+            :len (- (js2-current-token-end) beg)
+            :module-id (js2-current-token-string)))
+          (t
+           (let* ((import-clause (js2-parse-import-clause))
+                  (from-clause (and import-clause (js2-parse-from-clause)))
+                  (module-id (when from-clause (js2-from-clause-node-module-id from-clause)))
+                  (node (make-js2-import-node
+                         :pos beg
+                         :len (- (js2-current-token-end) beg)
+                         :import import-clause
+                         :from from-clause
+                         :module-id module-id)))
+             (when import-clause
+               (js2-node-add-children node import-clause))
+             (when from-clause
+               (js2-node-add-children node from-clause))
+             node)))))
+
+(defun js2-parse-import-clause ()
+  "Parse the bindings in an import statement.
+This can take many forms:
+
+ImportedDefaultBinding -> 'foo'
+NameSpaceImport -> '* as lib'
+NamedImports -> '{foo as bar, bang}'
+ImportedDefaultBinding , NameSpaceImport -> 'foo, * as lib'
+ImportedDefaultBinding , NamedImports -> 'foo, {bar, baz as bif}'
+
+Try to match namespace imports and named imports first because nothing can
+come after them. If it is an imported default binding, then it could have named
+imports or a namespace import that follows it.
+"
+  (let* ((beg (js2-current-token-beg))
+         (clause (make-js2-import-clause-node
+                  :pos beg))
+         (children (list)))
+    (cond
+     ((js2-match-token js2-MUL)
+      (let ((ns-import (js2-parse-namespace-import)))
+        (when ns-import
+          (let ((name-node (js2-namespace-import-node-name ns-import)))
+            (js2-define-symbol
+             js2-LET (js2-name-node-name name-node) name-node t)))
+        (setf (js2-import-clause-node-namespace-import clause) ns-import)
+        (push ns-import children)))
+     ((js2-match-token js2-LC)
+      (let ((imports (js2-parse-export-bindings t)))
+        (setf (js2-import-clause-node-named-imports clause) imports)
+        (dolist (import imports)
+          (push import children)
+          (let ((name-node (js2-export-binding-node-local-name import)))
+            (when name-node
+              (js2-define-symbol
+               js2-LET (js2-name-node-name name-node) name-node t))))))
+     ((= (js2-peek-token) js2-NAME)
+      (let ((binding (js2-maybe-parse-export-binding)))
+        (let ((node-name (js2-export-binding-node-local-name binding)))
+          (js2-define-symbol js2-LET (js2-name-node-name node-name) node-name t))
+        (setf (js2-import-clause-node-default-binding clause) binding)
+        (push binding children))
+      (when (js2-match-token js2-COMMA)
+        (cond
+         ((js2-match-token js2-MUL)
+          (let ((ns-import (js2-parse-namespace-import)))
+            (let ((name-node (js2-namespace-import-node-name ns-import)))
+              (js2-define-symbol
+               js2-LET (js2-name-node-name name-node) name-node t))
+            (setf (js2-import-clause-node-namespace-import clause) ns-import)
+            (push ns-import children)))
+         ((js2-match-token js2-LC)
+          (let ((imports (js2-parse-export-bindings t)))
+            (setf (js2-import-clause-node-named-imports clause) imports)
+            (dolist (import imports)
+              (push import children)
+              (let ((name-node (js2-export-binding-node-local-name import)))
+                (when name-node
+                  (js2-define-symbol
+                   js2-LET (js2-name-node-name name-node) name-node t))))))
+         (t (js2-report-error "msg.syntax")))))
+     (t (js2-report-error "msg.mod.declaration.after.import")))
+    (setf (js2-node-len clause) (- (js2-current-token-end) beg))
+    (apply #'js2-node-add-children clause children)
+    clause))
+
+(defun js2-parse-namespace-import ()
+  "Parse a namespace import expression such as  '* as bar'.
+The current token must be js2-MUL."
+  (let ((beg (js2-current-token-beg)))
+    (when (js2-must-match js2-NAME "msg.syntax")
+        (if (equal "as" (js2-current-token-string))
+            (when (js2-must-match-prop-name "msg.syntax")
+              (let ((node (make-js2-namespace-import-node
+                           :pos beg
+                           :len (- (js2-current-token-end) beg)
+                           :name (make-js2-name-node
+                                  :pos (js2-current-token-beg)
+                                  :len (js2-current-token-end)
+                                  :name (js2-current-token-string)))))
+                (js2-node-add-children node (js2-namespace-import-node-name node))
+                node))
+          (js2-unget-token)
+          (js2-report-error "msg.syntax")))))
+
+
+(defun js2-parse-from-clause ()
+  "Parse the from clause in an import or export statement. E.g. from 'src/lib'"
+  (when (js2-must-match-name "msg.mod.from.after.import.spec.set")
+    (let ((beg (js2-current-token-beg)))
+      (if (equal "from" (js2-current-token-string))
+          (cond
+           ((js2-match-token js2-STRING)
+            (make-js2-from-clause-node
+             :pos beg
+             :len (- (js2-current-token-end) beg)
+             :module-id (js2-current-token-string)
+             :metadata-p nil))
+           ((js2-match-token js2-THIS)
+            (when (js2-must-match-name "msg.mod.spec.after.from")
+              (if (equal "module" (js2-current-token-string))
+                  (make-js2-from-clause-node
+                   :pos beg
+                   :len (- (js2-current-token-end) beg)
+                   :module-id "this"
+                   :metadata-p t)
+                (js2-unget-token)
+                (js2-unget-token)
+                (js2-report-error "msg.mod.spec.after.from")
+                nil)))
+           (t (js2-report-error "msg.mod.spec.after.from") nil))
+        (js2-unget-token)
+        (js2-report-error "msg.mod.from.after.import.spec.set")
+        nil))))
+
+(defun js2-parse-export-bindings (&optional import-p)
+  "Parse a list of export binding expressions such as {}, {foo, bar}, and
+{foo as bar, baz as bang}. The current token must be
+js2-LC. Return a lisp list of js2-export-binding-node"
+  (let ((bindings (list)))
+    (while
+        (let ((binding (js2-maybe-parse-export-binding)))
+          (when binding
+            (push binding bindings))
+          (js2-match-token js2-COMMA)))
+    (when (js2-must-match js2-RC (if import-p
+                                     "msg.mod.rc.after.import.spec.list"
+                                   "msg.mod.rc.after.export.spec.list"))
+      (reverse bindings))))
+
+(defun js2-maybe-parse-export-binding ()
+  "Attempt to parse a binding expression found inside an import/export statement.
+This can take the form of either as single js2-NAME token as in 'foo' or as in a
+rebinding expression 'bar as foo'. If it matches, it will return an instance of
+js2-export-binding-node and consume all the tokens. If it does not match, it
+consumes no tokens."
+  (let ((extern-name (when (js2-match-prop-name) (js2-current-token-string)))
+        (beg (js2-current-token-beg))
+        (extern-name-len (js2-current-token-len))
+        (is-reserved-name (or (= (js2-current-token-type) js2-RESERVED)
+                              (aref js2-kwd-tokens (js2-current-token-type)))))
+    (if extern-name
+        (let ((as (and (js2-match-token js2-NAME) (js2-current-token-string))))
+          (if (and as (equal "as" (js2-current-token-string)))
+              (let ((name
+                     (or
+                      (and (js2-match-token js2-DEFAULT) "default")
+                      (and (js2-match-token js2-NAME) (js2-current-token-string)))))
+                (if name
+                    (let ((node (make-js2-export-binding-node
+                                 :pos beg
+                                 :len (- (js2-current-token-end) beg)
+                                 :local-name (make-js2-name-node
+                                              :name name
+                                              :pos (js2-current-token-beg)
+                                              :len (js2-current-token-len))
+                                 :extern-name (make-js2-name-node
+                                               :name extern-name
+                                               :pos beg
+                                               :len extern-name-len))))
+                      (js2-node-add-children
+                       node
+                       (js2-export-binding-node-local-name node)
+                       (js2-export-binding-node-extern-name node))
+                      node)
+                  (js2-unget-token)
+                  nil))
+            (when as (js2-unget-token))
+            (let* ((name-node (make-js2-name-node
+                              :name (js2-current-token-string)
+                              :pos (js2-current-token-beg)
+                              :len (js2-current-token-len)))
+                  (node (make-js2-export-binding-node
+                         :pos (js2-current-token-beg)
+                         :len (js2-current-token-len)
+                         :local-name name-node
+                         :extern-name name-node)))
+              (when is-reserved-name
+                (js2-report-error "msg.mod.as.after.reserved.word" extern-name))
+              (js2-node-add-children node name-node)
+              node)))
+      nil)))
+
+(defun js2-parse-switch ()
+  "Parser for switch-statement.  Last matched token must be js2-SWITCH."
+  (let ((pos (js2-current-token-beg))
+        tt pn discriminant has-default case-expr case-node
+        case-pos cases stmt lp)
+    (if (js2-must-match js2-LP "msg.no.paren.switch")
+        (setq lp (js2-current-token-beg)))
+    (setq discriminant (js2-parse-expr)
+          pn (make-js2-switch-node :discriminant discriminant
+                                   :pos pos
+                                   :lp (js2-relpos lp pos)))
+    (js2-node-add-children pn discriminant)
+    (js2-enter-switch pn)
+    (unwind-protect
+        (progn
+          (if (js2-must-match js2-RP "msg.no.paren.after.switch")
+              (setf (js2-switch-node-rp pn) (- (js2-current-token-beg) pos)))
+          (js2-must-match js2-LC "msg.no.brace.switch")
+          (catch 'break
+            (while t
+              (setq tt (js2-next-token)
+                    case-pos (js2-current-token-beg))
+              (cond
+               ((= tt js2-RC)
+                (setf (js2-node-len pn) (- (js2-current-token-end) pos))
+                (throw 'break nil))  ; done
+               ((= tt js2-CASE)
+                (setq case-expr (js2-parse-expr))
+                (js2-must-match js2-COLON "msg.no.colon.case"))
+               ((= tt js2-DEFAULT)
+                (if has-default
+                    (js2-report-error "msg.double.switch.default"))
+                (setq has-default t
+                      case-expr nil)
+                (js2-must-match js2-COLON "msg.no.colon.case"))
+               (t
+                (js2-report-error "msg.bad.switch")
+                (throw 'break nil)))
+              (setq case-node (make-js2-case-node :pos case-pos
+                                                  :len (- (js2-current-token-end) case-pos)
+                                                  :expr case-expr))
+              (js2-node-add-children case-node case-expr)
+              (while (and (/= (setq tt (js2-peek-token)) js2-RC)
+                          (/= tt js2-CASE)
+                          (/= tt js2-DEFAULT)
+                          (/= tt js2-EOF))
+                (setf stmt (js2-parse-statement)
+                      (js2-node-len case-node) (- (js2-node-end stmt) case-pos))
+                (js2-block-node-push case-node stmt))
+              (push case-node cases)))
+          ;; add cases last, as pushing reverses the order to be correct
+          (dolist (kid cases)
+            (js2-node-add-children pn kid)
+            (push kid (js2-switch-node-cases pn)))
+          pn)  ; return value
+      (js2-exit-switch))))
+
+(defun js2-parse-while ()
+  "Parser for while-statement.  Last matched token must be js2-WHILE."
+  (let ((pos (js2-current-token-beg))
+        (pn (make-js2-while-node))
+        cond body)
+    (js2-enter-loop pn)
+    (unwind-protect
+        (progn
+          (setf cond (js2-parse-condition)
+                (js2-while-node-condition pn) (car cond)
+                body (js2-parse-statement)
+                (js2-while-node-body pn) body
+                (js2-node-len pn) (- (js2-node-end body) pos)
+                (js2-while-node-lp pn) (js2-relpos (cl-second cond) pos)
+                (js2-while-node-rp pn) (js2-relpos (cl-third cond) pos))
+          (js2-node-add-children pn body (car cond)))
+      (js2-exit-loop))
+    pn))
+
+(defun js2-parse-do ()
+  "Parser for do-statement.  Last matched token must be js2-DO."
+  (let ((pos (js2-current-token-beg))
+        (pn (make-js2-do-node))
+        cond body end)
+    (js2-enter-loop pn)
+    (unwind-protect
+        (progn
+          (setq body (js2-parse-statement))
+          (js2-must-match js2-WHILE "msg.no.while.do")
+          (setf (js2-do-node-while-pos pn) (- (js2-current-token-beg) pos)
+                cond (js2-parse-condition)
+                (js2-do-node-condition pn) (car cond)
+                (js2-do-node-body pn) body
+                end js2-ts-cursor
+                (js2-do-node-lp pn) (js2-relpos (cl-second cond) pos)
+                (js2-do-node-rp pn) (js2-relpos (cl-third cond) pos))
+          (js2-node-add-children pn (car cond) body))
+      (js2-exit-loop))
+    ;; Always auto-insert semicolon to follow SpiderMonkey:
+    ;; It is required by ECMAScript but is ignored by the rest of
+    ;; world; see bug 238945
+    (if (js2-match-token js2-SEMI)
+        (setq end js2-ts-cursor))
+    (setf (js2-node-len pn) (- end pos))
+    pn))
+
+(defun js2-parse-export ()
+  "Parse an export statement.
+The Last matched token must be js2-EXPORT. Currently, the 'default' and 'expr'
+expressions should only be either hoistable expressions (function or generator)
+or assignment expressions, but there is no checking to enforce that and so it
+will parse without error a small subset of
+invalid export statements."
+  (unless (js2-ast-root-p js2-current-scope)
+    (js2-report-error "msg.mod.export.decl.at.top.level"))
+  (let ((beg (js2-current-token-beg))
+        (children (list))
+        exports-list from-clause declaration default)
+    (cond
+     ((js2-match-token js2-MUL)
+      (setq from-clause (js2-parse-from-clause))
+      (when from-clause
+        (push from-clause children)))
+     ((js2-match-token js2-LC)
+      (setq exports-list (js2-parse-export-bindings))
+      (when exports-list
+        (dolist (export exports-list)
+          (push export children)))
+      (when (js2-match-token js2-NAME)
+        (if (equal "from" (js2-current-token-string))
+            (progn
+              (js2-unget-token)
+              (setq from-clause (js2-parse-from-clause)))
+          (js2-unget-token))))
+     ((js2-match-token js2-DEFAULT)
+      (setq default (js2-parse-expr)))
+     ((or (js2-match-token js2-VAR) (js2-match-token js2-CONST) (js2-match-token js2-LET))
+      (setq declaration (js2-parse-variables (js2-current-token-type) (js2-current-token-beg))))
+     (t
+      (setq declaration (js2-parse-expr))))
+    (when from-clause
+      (push from-clause children))
+    (when declaration
+      (push declaration children))
+    (when default
+      (push default children))
+    (let ((node (make-js2-export-node
+                  :pos beg
+                  :len (- (js2-current-token-end) beg)
+                  :exports-list exports-list
+                  :from-clause from-clause
+                  :declaration declaration
+                  :default default)))
+      (apply #'js2-node-add-children node children)
+      node)))
+
+(defun js2-parse-for ()
+  "Parse a for, for-in or for each-in statement.
+Last matched token must be js2-FOR."
+  (let ((for-pos (js2-current-token-beg))
+        pn is-for-each is-for-in-or-of is-for-of
+        in-pos each-pos tmp-pos
+        init  ; Node init is also foo in 'foo in object'.
+        cond  ; Node cond is also object in 'foo in object'.
+        incr  ; 3rd section of for-loop initializer.
+        body tt lp rp)
+    ;; See if this is a for each () instead of just a for ()
+    (when (js2-match-token js2-NAME)
+      (if (string= "each" (js2-current-token-string))
+          (progn
+            (setq is-for-each t
+                  each-pos (- (js2-current-token-beg) for-pos)) ; relative
+            (js2-record-face 'font-lock-keyword-face))
+        (js2-report-error "msg.no.paren.for")))
+    (if (js2-must-match js2-LP "msg.no.paren.for")
+        (setq lp (- (js2-current-token-beg) for-pos)))
+    (setq tt (js2-get-token))
+    ;; 'for' makes local scope
+    (js2-push-scope (make-js2-scope))
+    (unwind-protect
+        ;; parse init clause
+        (let ((js2-in-for-init t))  ; set as dynamic variable
+          (cond
+           ((= tt js2-SEMI)
+            (js2-unget-token)
+            (setq init (make-js2-empty-expr-node)))
+           ((or (= tt js2-VAR) (= tt js2-LET))
+            (setq init (js2-parse-variables tt (js2-current-token-beg))))
+           (t
+            (js2-unget-token)
+            (setq init (js2-parse-expr)))))
+      (if (or (js2-match-token js2-IN)
+              (and (>= js2-language-version 200)
+                   (js2-match-contextual-kwd "of")
+                   (setq is-for-of t)))
+          (setq is-for-in-or-of t
+                in-pos (- (js2-current-token-beg) for-pos)
+                ;; scope of iteration target object is not the scope we've created above.
+                ;; stash current scope temporary.
+                cond (let ((js2-current-scope (js2-scope-parent-scope js2-current-scope)))
+                       (js2-parse-expr)))  ; object over which we're iterating
+        ;; else ordinary for loop - parse cond and incr
+        (js2-must-match js2-SEMI "msg.no.semi.for")
+        (setq cond (if (= (js2-peek-token) js2-SEMI)
+                       (make-js2-empty-expr-node) ; no loop condition
+                     (js2-parse-expr)))
+        (js2-must-match js2-SEMI "msg.no.semi.for.cond")
+        (setq tmp-pos (js2-current-token-end)
+              incr (if (= (js2-peek-token) js2-RP)
+                       (make-js2-empty-expr-node :pos tmp-pos)
+                     (js2-parse-expr))))
+      (if (js2-must-match js2-RP "msg.no.paren.for.ctrl")
+          (setq rp (- (js2-current-token-beg) for-pos)))
+      (if (not is-for-in-or-of)
+          (setq pn (make-js2-for-node :init init
+                                      :condition cond
+                                      :update incr
+                                      :lp lp
+                                      :rp rp))
+        ;; cond could be null if 'in obj' got eaten by the init node.
+        (if (js2-infix-node-p init)
+            ;; it was (foo in bar) instead of (var foo in bar)
+            (setq cond (js2-infix-node-right init)
+                  init (js2-infix-node-left init))
+          (if (and (js2-var-decl-node-p init)
+                   (> (length (js2-var-decl-node-kids init)) 1))
+              (js2-report-error "msg.mult.index")))
+        (setq pn (make-js2-for-in-node :iterator init
+                                       :object cond
+                                       :in-pos in-pos
+                                       :foreach-p is-for-each
+                                       :each-pos each-pos
+                                       :forof-p is-for-of
+                                       :lp lp
+                                       :rp rp)))
+      (unwind-protect
+          (progn
+            (js2-enter-loop pn)
+            ;; We have to parse the body -after- creating the loop node,
+            ;; so that the loop node appears in the js2-loop-set, allowing
+            ;; break/continue statements to find the enclosing loop.
+            (setf body (js2-parse-statement)
+                  (js2-loop-node-body pn) body
+                  (js2-node-pos pn) for-pos
+                  (js2-node-len pn) (- (js2-node-end body) for-pos))
+            (js2-node-add-children pn init cond incr body))
+        ;; finally
+        (js2-exit-loop))
+      (js2-pop-scope))
+    pn))
+
+(defun js2-parse-try ()
+  "Parse a try statement.  Last matched token must be js2-TRY."
+  (let ((try-pos (js2-current-token-beg))
+        try-end
+        try-block
+        catch-blocks
+        finally-block
+        saw-default-catch
+        peek)
+    (if (/= (js2-peek-token) js2-LC)
+        (js2-report-error "msg.no.brace.try"))
+    (setq try-block (js2-parse-statement)
+          try-end (js2-node-end try-block)
+          peek (js2-peek-token))
+    (cond
+     ((= peek js2-CATCH)
+      (while (js2-match-token js2-CATCH)
+        (let* ((catch-pos (js2-current-token-beg))
+               (catch-node (make-js2-catch-node :pos catch-pos))
+               param
+               guard-kwd
+               catch-cond
+               lp rp)
+          (if saw-default-catch
+              (js2-report-error "msg.catch.unreachable"))
+          (if (js2-must-match js2-LP "msg.no.paren.catch")
+              (setq lp (- (js2-current-token-beg) catch-pos)))
+          (js2-push-scope catch-node)
+          (let ((tt (js2-peek-token)))
+            (cond
+             ;; Destructuring pattern:
+             ;;     catch ({ message, file }) { ... }
+             ((or (= tt js2-LB) (= tt js2-LC))
+              (js2-get-token)
+              (setq param (js2-parse-destruct-primary-expr))
+              (js2-define-destruct-symbols param js2-LET nil))
+             ;; Simple name.
+             (t
+              (js2-must-match-name "msg.bad.catchcond")
+              (setq param (js2-create-name-node))
+              (js2-define-symbol js2-LET (js2-current-token-string) param))))
+          ;; Catch condition.
+          (if (js2-match-token js2-IF)
+              (setq guard-kwd (- (js2-current-token-beg) catch-pos)
+                    catch-cond (js2-parse-expr))
+            (setq saw-default-catch t))
+          (if (js2-must-match js2-RP "msg.bad.catchcond")
+              (setq rp (- (js2-current-token-beg) catch-pos)))
+          (js2-must-match js2-LC "msg.no.brace.catchblock")
+          (js2-parse-statements catch-node)
+          (if (js2-must-match js2-RC "msg.no.brace.after.body")
+              (setq try-end (js2-current-token-end)))
+          (js2-pop-scope)
+          (setf (js2-node-len catch-node) (- try-end catch-pos)
+                (js2-catch-node-param catch-node) param
+                (js2-catch-node-guard-expr catch-node) catch-cond
+                (js2-catch-node-guard-kwd catch-node) guard-kwd
+                (js2-catch-node-lp catch-node) lp
+                (js2-catch-node-rp catch-node) rp)
+          (js2-node-add-children catch-node param catch-cond)
+          (push catch-node catch-blocks))))
+     ((/= peek js2-FINALLY)
+      (js2-must-match js2-FINALLY "msg.try.no.catchfinally"
+                      (js2-node-pos try-block)
+                      (- (setq try-end (js2-node-end try-block))
+                         (js2-node-pos try-block)))))
+    (when (js2-match-token js2-FINALLY)
+      (let ((finally-pos (js2-current-token-beg))
+            (block (js2-parse-statement)))
+        (setq try-end (js2-node-end block)
+              finally-block (make-js2-finally-node :pos finally-pos
+                                                   :len (- try-end finally-pos)
+                                                   :body block))
+        (js2-node-add-children finally-block block)))
+    (let ((pn (make-js2-try-node :pos try-pos
+                                 :len (- try-end try-pos)
+                                 :try-block try-block
+                                 :finally-block finally-block)))
+      (js2-node-add-children pn try-block finally-block)
+      ;; Push them onto the try-node, which reverses and corrects their order.
+      (dolist (cb catch-blocks)
+        (js2-node-add-children pn cb)
+        (push cb (js2-try-node-catch-clauses pn)))
+      pn)))
+
+(defun js2-parse-throw ()
+  "Parser for throw-statement.  Last matched token must be js2-THROW."
+  (let ((pos (js2-current-token-beg))
+        expr pn)
+    (if (= (js2-peek-token-or-eol) js2-EOL)
+        ;; ECMAScript does not allow new lines before throw expression,
+        ;; see bug 256617
+        (js2-report-error "msg.bad.throw.eol"))
+    (setq expr (js2-parse-expr)
+          pn (make-js2-throw-node :pos pos
+                                  :len (- (js2-node-end expr) pos)
+                                  :expr expr))
+    (js2-node-add-children pn expr)
+    pn))
+
+(defun js2-match-jump-label-name (label-name)
+  "If break/continue specified a label, return that label's labeled stmt.
+Returns the corresponding `js2-labeled-stmt-node', or if LABEL-NAME
+does not match an existing label, reports an error and returns nil."
+  (let ((bundle (cdr (assoc label-name js2-label-set))))
+    (if (null bundle)
+        (js2-report-error "msg.undef.label"))
+    bundle))
+
+(defun js2-parse-break ()
+  "Parser for break-statement.  Last matched token must be js2-BREAK."
+  (let ((pos (js2-current-token-beg))
+        (end (js2-current-token-end))
+        break-target ; statement to break from
+        break-label  ; in "break foo", name-node representing the foo
+        labels       ; matching labeled statement to break to
+        pn)
+    (when (eq (js2-peek-token-or-eol) js2-NAME)
+      (js2-get-token)
+      (setq break-label (js2-create-name-node)
+            end (js2-node-end break-label)
+            ;; matchJumpLabelName only matches if there is one
+            labels (js2-match-jump-label-name (js2-current-token-string))
+            break-target (if labels (car (js2-labeled-stmt-node-labels labels)))))
+    (unless (or break-target break-label)
+      ;; no break target specified - try for innermost enclosing loop/switch
+      (if (null js2-loop-and-switch-set)
+          (unless break-label
+            (js2-report-error "msg.bad.break" nil pos (length "break")))
+        (setq break-target (car js2-loop-and-switch-set))))
+    (setq pn (make-js2-break-node :pos pos
+                                  :len (- end pos)
+                                  :label break-label
+                                  :target break-target))
+    (js2-node-add-children pn break-label)  ; but not break-target
+    pn))
+
+(defun js2-parse-continue ()
+  "Parser for continue-statement.  Last matched token must be js2-CONTINUE."
+  (let ((pos (js2-current-token-beg))
+        (end (js2-current-token-end))
+        label   ; optional user-specified label, a `js2-name-node'
+        labels  ; current matching labeled stmt, if any
+        target  ; the `js2-loop-node' target of this continue stmt
+        pn)
+    (when (= (js2-peek-token-or-eol) js2-NAME)
+      (js2-get-token)
+      (setq label (js2-create-name-node)
+            end (js2-node-end label)
+            ;; matchJumpLabelName only matches if there is one
+            labels (js2-match-jump-label-name (js2-current-token-string))))
+    (cond
+     ((null labels)  ; no current label to go to
+      (if (null js2-loop-set)  ; no loop to continue to
+          (js2-report-error "msg.continue.outside" nil pos
+                            (length "continue"))
+        (setq target (car js2-loop-set))))  ; innermost enclosing loop
+     (t
+      (if (js2-loop-node-p (js2-labeled-stmt-node-stmt labels))
+          (setq target (js2-labeled-stmt-node-stmt labels))
+        (js2-report-error "msg.continue.nonloop" nil pos (- end pos)))))
+    (setq pn (make-js2-continue-node :pos pos
+                                     :len (- end pos)
+                                     :label label
+                                     :target target))
+    (js2-node-add-children pn label)  ; but not target - it's not our child
+    pn))
+
+(defun js2-parse-with ()
+  "Parser for with-statement.  Last matched token must be js2-WITH."
+  (let ((pos (js2-current-token-beg))
+        obj body pn lp rp)
+    (if (js2-must-match js2-LP "msg.no.paren.with")
+        (setq lp (js2-current-token-beg)))
+    (setq obj (js2-parse-expr))
+    (if (js2-must-match js2-RP "msg.no.paren.after.with")
+        (setq rp (js2-current-token-beg)))
+    (let ((js2-nesting-of-with (1+ js2-nesting-of-with)))
+        (setq body (js2-parse-statement)))
+    (setq pn (make-js2-with-node :pos pos
+                                 :len (- (js2-node-end body) pos)
+                                 :object obj
+                                 :body body
+                                 :lp (js2-relpos lp pos)
+                                 :rp (js2-relpos rp pos)))
+    (js2-node-add-children pn obj body)
+    pn))
+
+(defun js2-parse-const-var ()
+  "Parser for var- or const-statement.
+Last matched token must be js2-CONST or js2-VAR."
+  (let ((tt (js2-current-token-type))
+        (pos (js2-current-token-beg))
+        expr pn)
+    (setq expr (js2-parse-variables tt (js2-current-token-beg))
+          pn (make-js2-expr-stmt-node :pos pos
+                                      :len (- (js2-node-end expr) pos)
+                                      :expr expr))
+    (js2-node-add-children pn expr)
+    pn))
+
+(defun js2-wrap-with-expr-stmt (pos expr &optional add-child)
+  (let ((pn (make-js2-expr-stmt-node :pos pos
+                                     :len (js2-node-len expr)
+                                     :type (if (js2-inside-function)
+                                               js2-EXPR_VOID
+                                             js2-EXPR_RESULT)
+                                     :expr expr)))
+    (if add-child
+        (js2-node-add-children pn expr))
+    pn))
+
+(defun js2-parse-let-stmt ()
+  "Parser for let-statement.  Last matched token must be js2-LET."
+  (let ((pos (js2-current-token-beg))
+        expr pn)
+    (if (= (js2-peek-token) js2-LP)
+        ;; let expression in statement context
+        (setq expr (js2-parse-let pos 'statement)
+              pn (js2-wrap-with-expr-stmt pos expr t))
+      ;; else we're looking at a statement like let x=6, y=7;
+      (setf expr (js2-parse-variables js2-LET pos)
+            pn (js2-wrap-with-expr-stmt pos expr t)
+            (js2-node-type pn) js2-EXPR_RESULT))
+    pn))
+
+(defun js2-parse-ret-yield ()
+  (js2-parse-return-or-yield (js2-current-token-type) nil))
+
+(defconst js2-parse-return-stmt-enders
+  (list js2-SEMI js2-RC js2-EOF js2-EOL js2-ERROR js2-RB js2-RP js2-YIELD))
+
+(defsubst js2-now-all-set (before after mask)
+  "Return whether or not the bits in the mask have changed to all set.
+BEFORE is bits before change, AFTER is bits after change, and MASK is
+the mask for bits.  Returns t if all the bits in the mask are set in AFTER
+but not BEFORE."
+  (and (/= (logand before mask) mask)
+       (= (logand after mask) mask)))
+
+(defun js2-parse-return-or-yield (tt expr-context)
+  (let* ((pos (js2-current-token-beg))
+         (end (js2-current-token-end))
+         (before js2-end-flags)
+         (inside-function (js2-inside-function))
+         (gen-type (and inside-function (js2-function-node-generator-type
+                                         js2-current-script-or-fn)))
+         e ret name yield-star-p)
+    (unless inside-function
+      (js2-report-error (if (eq tt js2-RETURN)
+                            "msg.bad.return"
+                          "msg.bad.yield")))
+    (when (and inside-function
+               (eq gen-type 'STAR)
+               (js2-match-token js2-MUL))
+      (setq yield-star-p t))
+    ;; This is ugly, but we don't want to require a semicolon.
+    (unless (memq (js2-peek-token-or-eol) js2-parse-return-stmt-enders)
+      (setq e (js2-parse-expr)
+            end (js2-node-end e)))
+    (cond
+     ((eq tt js2-RETURN)
+      (js2-set-flag js2-end-flags (if (null e)
+                                      js2-end-returns
+                                    js2-end-returns-value))
+      (setq ret (make-js2-return-node :pos pos
+                                      :len (- end pos)
+                                      :retval e))
+      (js2-node-add-children ret e)
+      ;; See if we need a strict mode warning.
+      ;; TODO:  The analysis done by `js2-has-consistent-return-usage' is
+      ;; more thorough and accurate than this before/after flag check.
+      ;; E.g. if there's a finally-block that always returns, we shouldn't
+      ;; show a warning generated by inconsistent returns in the catch blocks.
+      ;; Basically `js2-has-consistent-return-usage' needs to keep more state,
+      ;; so we know which returns/yields to highlight, and we should get rid of
+      ;; all the checking in `js2-parse-return-or-yield'.
+      (if (and js2-strict-inconsistent-return-warning
+               (js2-now-all-set before js2-end-flags
+                                (logior js2-end-returns js2-end-returns-value)))
+          (js2-add-strict-warning "msg.return.inconsistent" nil pos end)))
+     ((eq gen-type 'COMPREHENSION)
+      ;; FIXME: We should probably switch to saving and using lastYieldOffset,
+      ;; like SpiderMonkey does.
+      (js2-report-error "msg.syntax" nil pos 5))
+     (t
+      (setq ret (make-js2-yield-node :pos pos
+                                     :len (- end pos)
+                                     :value e
+                                     :star-p yield-star-p))
+      (js2-node-add-children ret e)
+      (unless expr-context
+        (setq e ret
+              ret (js2-wrap-with-expr-stmt pos e t))
+      (js2-set-requires-activation)
+      (js2-set-is-generator))))
+    ;; see if we are mixing yields and value returns.
+    (when (and inside-function
+               (js2-flag-set-p js2-end-flags js2-end-returns-value)
+               (eq (js2-function-node-generator-type js2-current-script-or-fn)
+                   'LEGACY))
+      (setq name (js2-function-name js2-current-script-or-fn))
+      (if (zerop (length name))
+          (js2-report-error "msg.anon.generator.returns" nil pos (- end pos))
+        (js2-report-error "msg.generator.returns" name pos (- end pos))))
+    ret))
+
+(defun js2-parse-debugger ()
+  (make-js2-keyword-node :type js2-DEBUGGER))
+
+(defun js2-parse-block ()
+  "Parser for a curly-delimited statement block.
+Last token matched must be `js2-LC'."
+  (let ((pos (js2-current-token-beg))
+        (pn (make-js2-scope)))
+    (js2-push-scope pn)
+    (unwind-protect
+        (progn
+          (js2-parse-statements pn)
+          (js2-must-match js2-RC "msg.no.brace.block")
+          (setf (js2-node-len pn) (- (js2-current-token-end) pos)))
+      (js2-pop-scope))
+    pn))
+
+;; For `js2-ERROR' too, to have a node for error recovery to work on.
+(defun js2-parse-semi ()
+  "Parse a statement or handle an error.
+Current token type is `js2-SEMI' or `js2-ERROR'."
+  (let ((tt (js2-current-token-type)) pos len)
+    (if (eq tt js2-SEMI)
+        (make-js2-empty-expr-node :len 1)
+      (setq pos (js2-current-token-beg)
+            len (- (js2-current-token-end) pos))
+      (js2-report-error "msg.syntax" nil pos len)
+      (make-js2-error-node :pos pos :len len))))
+
+(defun js2-parse-default-xml-namespace ()
+  "Parse a `default xml namespace = <expr>' e4x statement."
+  (let ((pos (js2-current-token-beg))
+        end len expr unary)
+    (js2-must-have-xml)
+    (js2-set-requires-activation)
+    (setq len (- js2-ts-cursor pos))
+    (unless (and (js2-match-token js2-NAME)
+                 (string= (js2-current-token-string) "xml"))
+      (js2-report-error "msg.bad.namespace" nil pos len))
+    (unless (and (js2-match-token js2-NAME)
+                 (string= (js2-current-token-string) "namespace"))
+      (js2-report-error "msg.bad.namespace" nil pos len))
+    (unless (js2-match-token js2-ASSIGN)
+      (js2-report-error "msg.bad.namespace" nil pos len))
+    (setq expr (js2-parse-expr)
+          end (js2-node-end expr)
+          unary (make-js2-unary-node :type js2-DEFAULTNAMESPACE
+                                     :pos pos
+                                     :len (- end pos)
+                                     :operand expr))
+    (js2-node-add-children unary expr)
+    (make-js2-expr-stmt-node :pos pos
+                             :len (- end pos)
+                             :expr unary)))
+
+(defun js2-record-label (label bundle)
+  ;; current token should be colon that `js2-parse-primary-expr' left untouched
+  (js2-get-token)
+  (let ((name (js2-label-node-name label))
+        labeled-stmt
+        dup)
+    (when (setq labeled-stmt (cdr (assoc name js2-label-set)))
+      ;; flag both labels if possible when used in editing mode
+      (if (and js2-parse-ide-mode
+               (setq dup (js2-get-label-by-name labeled-stmt name)))
+          (js2-report-error "msg.dup.label" nil
+                            (js2-node-abs-pos dup) (js2-node-len dup)))
+      (js2-report-error "msg.dup.label" nil
+                        (js2-node-pos label) (js2-node-len label)))
+    (js2-labeled-stmt-node-add-label bundle label)
+    (js2-node-add-children bundle label)
+    ;; Add one reference to the bundle per label in `js2-label-set'
+    (push (cons name bundle) js2-label-set)))
+
+(defun js2-parse-name-or-label ()
+  "Parser for identifier or label.  Last token matched must be js2-NAME.
+Called when we found a name in a statement context.  If it's a label, we gather
+up any following labels and the next non-label statement into a
+`js2-labeled-stmt-node' bundle and return that.  Otherwise we parse an
+expression and return it wrapped in a `js2-expr-stmt-node'."
+  (let ((pos (js2-current-token-beg))
+        expr stmt bundle
+        (continue t))
+    ;; set check for label and call down to `js2-parse-primary-expr'
+    (setq expr (js2-maybe-parse-label))
+    (if (null expr)
+        ;; Parse the non-label expression and wrap with expression stmt.
+        (js2-wrap-with-expr-stmt pos (js2-parse-expr) t)
+      ;; else parsed a label
+      (setq bundle (make-js2-labeled-stmt-node :pos pos))
+      (js2-record-label expr bundle)
+      ;; look for more labels
+      (while (and continue (= (js2-get-token) js2-NAME))
+        (if (setq expr (js2-maybe-parse-label))
+            (js2-record-label expr bundle)
+          (setq expr (js2-parse-expr)
+                stmt (js2-wrap-with-expr-stmt (js2-node-pos expr) expr t)
+                continue nil)
+          (js2-auto-insert-semicolon stmt)))
+      ;; no more labels; now parse the labeled statement
+      (unwind-protect
+            (unless stmt
+              (let ((js2-labeled-stmt bundle))  ; bind dynamically
+                (js2-unget-token)
+                (setq stmt (js2-statement-helper))))
+        ;; remove the labels for this statement from the global set
+        (dolist (label (js2-labeled-stmt-node-labels bundle))
+          (setq js2-label-set (remove label js2-label-set))))
+      (setf (js2-labeled-stmt-node-stmt bundle) stmt
+            (js2-node-len bundle) (- (js2-node-end stmt) pos))
+      (js2-node-add-children bundle stmt)
+      bundle)))
+
+(defun js2-maybe-parse-label ()
+  (cl-assert (= (js2-current-token-type) js2-NAME))
+  (let (label-pos
+        (next-tt (js2-get-token))
+        (label-end (js2-current-token-end)))
+    ;; Do not consume colon, it is used as unwind indicator
+    ;; to return to statementHelper.
+    (js2-unget-token)
+    (if (= next-tt js2-COLON)
+        (prog2
+            (setq label-pos (js2-current-token-beg))
+            (make-js2-label-node :pos label-pos
+                                 :len (- label-end label-pos)
+                                 :name (js2-current-token-string))
+          (js2-set-face label-pos
+                        label-end
+                        'font-lock-variable-name-face 'record))
+      ;; Backtrack from the name token, too.
+      (js2-unget-token)
+      nil)))
+
+(defun js2-parse-expr-stmt ()
+  "Default parser in statement context, if no recognized statement found."
+  (js2-wrap-with-expr-stmt (js2-current-token-beg)
+                           (progn
+                             (js2-unget-token)
+                             (js2-parse-expr)) t))
+
+(defun js2-parse-variables (decl-type pos)
+  "Parse a comma-separated list of variable declarations.
+Could be a 'var', 'const' or 'let' expression, possibly in a for-loop initializer.
+
+DECL-TYPE is a token value: either VAR, CONST, or LET depending on context.
+For 'var' or 'const', the keyword should be the token last scanned.
+
+POS is the position where the node should start. It's sometimes the
+var/const/let keyword, and other times the beginning of the first token
+in the first variable declaration.
+
+Returns the parsed `js2-var-decl-node' expression node."
+  (let* ((result (make-js2-var-decl-node :decl-type decl-type
+                                         :pos pos))
+         destructuring kid-pos tt init name end nbeg nend vi
+         (continue t))
+    ;; Example:
+    ;; var foo = {a: 1, b: 2}, bar = [3, 4];
+    ;; var {b: s2, a: s1} = foo, x = 6, y, [s3, s4] = bar;
+    ;; var {a, b} = baz;
+    (while continue
+      (setq destructuring nil
+            name nil
+            tt (js2-get-token)
+            kid-pos (js2-current-token-beg)
+            end (js2-current-token-end)
+            init nil)
+      (if (or (= tt js2-LB) (= tt js2-LC))
+          ;; Destructuring assignment, e.g., var [a, b] = ...
+          (setq destructuring (js2-parse-destruct-primary-expr)
+                end (js2-node-end destructuring))
+        ;; Simple variable name
+        (js2-unget-token)
+        (when (js2-must-match-name "msg.bad.var")
+          (setq name (js2-create-name-node)
+                nbeg (js2-current-token-beg)
+                nend (js2-current-token-end)
+                end nend)
+          (js2-define-symbol decl-type (js2-current-token-string) name js2-in-for-init)))
+      (when (js2-match-token js2-ASSIGN)
+        (setq init (js2-parse-assign-expr)
+              end (js2-node-end init))
+        (js2-record-imenu-functions init name))
+      (when name
+        (js2-set-face nbeg nend (if (js2-function-node-p init)
+                                    'font-lock-function-name-face
+                                  'font-lock-variable-name-face)
+                      'record))
+      (setq vi (make-js2-var-init-node :pos kid-pos
+                                       :len (- end kid-pos)
+                                       :type decl-type))
+      (if destructuring
+          (progn
+            (if (and (null init) (not js2-in-for-init))
+                (js2-report-error "msg.destruct.assign.no.init"))
+            (js2-define-destruct-symbols destructuring
+                                         decl-type
+                                         'font-lock-variable-name-face)
+            (setf (js2-var-init-node-target vi) destructuring))
+        (setf (js2-var-init-node-target vi) name))
+      (setf (js2-var-init-node-initializer vi) init)
+      (js2-node-add-children vi name destructuring init)
+      (js2-block-node-push result vi)
+      (unless (js2-match-token js2-COMMA)
+        (setq continue nil)))
+    (setf (js2-node-len result) (- end pos))
+    result))
+
+(defun js2-parse-let (pos &optional stmt-p)
+  "Parse a let expression or statement.
+A let-expression is of the form `let (vars) expr'.
+A let-statment is of the form `let (vars) {statements}'.
+The third form of let is a variable declaration list, handled
+by `js2-parse-variables'."
+  (let ((pn (make-js2-let-node :pos pos))
+        beg vars body)
+    (if (js2-must-match js2-LP "msg.no.paren.after.let")
+        (setf (js2-let-node-lp pn) (- (js2-current-token-beg) pos)))
+    (js2-push-scope pn)
+    (unwind-protect
+        (progn
+          (setq vars (js2-parse-variables js2-LET (js2-current-token-beg)))
+          (if (js2-must-match js2-RP "msg.no.paren.let")
+              (setf (js2-let-node-rp pn) (- (js2-current-token-beg) pos)))
+          (if (and stmt-p (js2-match-token js2-LC))
+              ;; let statement
+              (progn
+                (setf beg (js2-current-token-beg)  ; position stmt at LC
+                      body (js2-parse-statements))
+                (js2-must-match js2-RC "msg.no.curly.let")
+                (setf (js2-node-len body) (- (js2-current-token-end) beg)
+                      (js2-node-len pn) (- (js2-current-token-end) pos)
+                      (js2-let-node-body pn) body
+                      (js2-node-type pn) js2-LET))
+            ;; let expression
+            (setf body (js2-parse-expr)
+                  (js2-node-len pn) (- (js2-node-end body) pos)
+                  (js2-let-node-body pn) body))
+          (setf (js2-let-node-vars pn) vars)
+          (js2-node-add-children pn vars body))
+      (js2-pop-scope))
+    pn))
+
+(defun js2-define-new-symbol (decl-type name node &optional scope)
+  (js2-scope-put-symbol (or scope js2-current-scope)
+                        name
+                        (make-js2-symbol decl-type name node)))
+
+(defun js2-define-symbol (decl-type name &optional node ignore-not-in-block)
+  "Define a symbol in the current scope.
+If NODE is non-nil, it is the AST node associated with the symbol."
+  (let* ((defining-scope (js2-get-defining-scope js2-current-scope name))
+         (symbol (if defining-scope
+                     (js2-scope-get-symbol defining-scope name)))
+         (sdt (if symbol (js2-symbol-decl-type symbol) -1))
+         (pos (if node (js2-node-abs-pos node)))
+         (len (if node (js2-node-len node))))
+    (cond
+     ((and symbol ; already defined
+           (or (= sdt js2-CONST) ; old version is const
+               (= decl-type js2-CONST) ; new version is const
+               ;; two let-bound vars in this block have same name
+               (and (= sdt js2-LET)
+                    (eq defining-scope js2-current-scope))))
+      (js2-report-error
+       (cond
+        ((= sdt js2-CONST) "msg.const.redecl")
+        ((= sdt js2-LET) "msg.let.redecl")
+        ((= sdt js2-VAR) "msg.var.redecl")
+        ((= sdt js2-FUNCTION) "msg.function.redecl")
+        (t "msg.parm.redecl"))
+       name pos len))
+     ((= decl-type js2-LET)
+      (if (and (not ignore-not-in-block)
+               (or (= (js2-node-type js2-current-scope) js2-IF)
+                   (js2-loop-node-p js2-current-scope)))
+          (js2-report-error "msg.let.decl.not.in.block")
+        (js2-define-new-symbol decl-type name node)))
+     ((or (= decl-type js2-VAR)
+          (= decl-type js2-CONST)
+          (= decl-type js2-FUNCTION))
+      (if symbol
+          (if (and js2-strict-var-redeclaration-warning (= sdt js2-VAR))
+              (js2-add-strict-warning "msg.var.redecl" name)
+            (if (and js2-strict-var-hides-function-arg-warning (= sdt js2-LP))
+                (js2-add-strict-warning "msg.var.hides.arg" name)))
+        (js2-define-new-symbol decl-type name node
+                               js2-current-script-or-fn)))
+     ((= decl-type js2-LP)
+      (if symbol
+          ;; must be duplicate parameter. Second parameter hides the
+          ;; first, so go ahead and add the second pararameter
+          (js2-report-warning "msg.dup.parms" name))
+      (js2-define-new-symbol decl-type name node))
+     (t (js2-code-bug)))))
+
+(defun js2-parse-paren-expr-or-generator-comp ()
+  (let ((px-pos (js2-current-token-beg)))
+    (if (and (>= js2-language-version 200)
+             (js2-match-token js2-FOR))
+        (js2-parse-generator-comp px-pos)
+      (let* ((js2-in-for-init nil)
+             (expr (js2-parse-expr))
+             (pn (make-js2-paren-node :pos px-pos
+                                      :expr expr
+                                      :len (- (js2-current-token-end)
+                                              px-pos))))
+        (js2-node-add-children pn (js2-paren-node-expr pn))
+        (js2-must-match js2-RP "msg.no.paren")
+        pn))))
+
+(defun js2-parse-expr (&optional oneshot)
+  (let* ((pn (js2-parse-assign-expr))
+         (pos (js2-node-pos pn))
+         left
+         right
+         op-pos)
+    (while (and (not oneshot)
+                (js2-match-token js2-COMMA))
+      (setq op-pos (- (js2-current-token-beg) pos))  ; relative
+      (if (= (js2-peek-token) js2-YIELD)
+          (js2-report-error "msg.yield.parenthesized"))
+      (setq right (js2-parse-assign-expr)
+            left pn
+            pn (make-js2-infix-node :type js2-COMMA
+                                    :pos pos
+                                    :len (- js2-ts-cursor pos)
+                                    :op-pos op-pos
+                                    :left left
+                                    :right right))
+      (js2-node-add-children pn left right))
+    pn))
+
+(defun js2-parse-assign-expr ()
+  (let ((tt (js2-get-token))
+        (pos (js2-current-token-beg))
+        pn left right op-pos
+        ts-state recorded-identifiers parsed-errors)
+    (if (= tt js2-YIELD)
+        (js2-parse-return-or-yield tt t)
+      ;; Save the tokenizer state in case we find an arrow function
+      ;; and have to rewind.
+      (setq ts-state (make-js2-ts-state)
+            recorded-identifiers js2-recorded-identifiers
+            parsed-errors js2-parsed-errors)
+      ;; not yield - parse assignment expression
+      (setq pn (js2-parse-cond-expr)
+            tt (js2-get-token))
+      (cond
+       ((and (<= js2-first-assign tt)
+             (<= tt js2-last-assign))
+        ;; tt express assignment (=, |=, ^=, ..., %=)
+        (setq op-pos (- (js2-current-token-beg) pos)  ; relative
+              left pn)
+        (setq right (js2-parse-assign-expr)
+              pn (make-js2-assign-node :type tt
+                                       :pos pos
+                                       :len (- (js2-node-end right) pos)
+                                       :op-pos op-pos
+                                       :left left
+                                       :right right))
+        (when js2-parse-ide-mode
+          (js2-highlight-assign-targets pn left right)
+          (js2-record-imenu-functions right left))
+        ;; do this last so ide checks above can use absolute positions
+        (js2-node-add-children pn left right))
+       ((and (= tt js2-ARROW)
+             (>= js2-language-version 200))
+        (js2-ts-seek ts-state)
+        (setq js2-recorded-identifiers recorded-identifiers
+              js2-parsed-errors parsed-errors)
+        (setq pn (js2-parse-function 'FUNCTION_ARROW (js2-current-token-beg) nil)))
+       (t
+        (js2-unget-token)))
+      pn)))
+
+(defun js2-parse-cond-expr ()
+  (let ((pos (js2-current-token-beg))
+        (pn (js2-parse-or-expr))
+        test-expr
+        if-true
+        if-false
+        q-pos
+        c-pos)
+    (when (js2-match-token js2-HOOK)
+      (setq q-pos (- (js2-current-token-beg) pos)
+            if-true (let (js2-in-for-init) (js2-parse-assign-expr)))
+      (js2-must-match js2-COLON "msg.no.colon.cond")
+      (setq c-pos (- (js2-current-token-beg) pos)
+            if-false (js2-parse-assign-expr)
+            test-expr pn
+            pn (make-js2-cond-node :pos pos
+                                   :len (- (js2-node-end if-false) pos)
+                                   :test-expr test-expr
+                                   :true-expr if-true
+                                   :false-expr if-false
+                                   :q-pos q-pos
+                                   :c-pos c-pos))
+      (js2-node-add-children pn test-expr if-true if-false))
+    pn))
+
+(defun js2-make-binary (type left parser)
+  "Helper for constructing a binary-operator AST node.
+LEFT is the left-side-expression, already parsed, and the
+binary operator should have just been matched.
+PARSER is a function to call to parse the right operand,
+or a `js2-node' struct if it has already been parsed.
+FIXME: The latter option is unused?"
+  (let* ((pos (js2-node-pos left))
+         (op-pos (- (js2-current-token-beg) pos))
+         (right (if (js2-node-p parser)
+                    parser
+                  (js2-get-token)
+                  (funcall parser)))
+         (pn (make-js2-infix-node :type type
+                                  :pos pos
+                                  :len (- (js2-node-end right) pos)
+                                  :op-pos op-pos
+                                  :left left
+                                  :right right)))
+    (js2-node-add-children pn left right)
+    pn))
+
+(defun js2-parse-or-expr ()
+  (let ((pn (js2-parse-and-expr)))
+    (when (js2-match-token js2-OR)
+      (setq pn (js2-make-binary js2-OR
+                                pn
+                                'js2-parse-or-expr)))
+    pn))
+
+(defun js2-parse-and-expr ()
+  (let ((pn (js2-parse-bit-or-expr)))
+    (when (js2-match-token js2-AND)
+      (setq pn (js2-make-binary js2-AND
+                                pn
+                                'js2-parse-and-expr)))
+    pn))
+
+(defun js2-parse-bit-or-expr ()
+  (let ((pn (js2-parse-bit-xor-expr)))
+    (while (js2-match-token js2-BITOR)
+      (setq pn (js2-make-binary js2-BITOR
+                                pn
+                                'js2-parse-bit-xor-expr)))
+    pn))
+
+(defun js2-parse-bit-xor-expr ()
+  (let ((pn (js2-parse-bit-and-expr)))
+    (while (js2-match-token js2-BITXOR)
+      (setq pn (js2-make-binary js2-BITXOR
+                                pn
+                                'js2-parse-bit-and-expr)))
+    pn))
+
+(defun js2-parse-bit-and-expr ()
+  (let ((pn (js2-parse-eq-expr)))
+    (while (js2-match-token js2-BITAND)
+      (setq pn (js2-make-binary js2-BITAND
+                                pn
+                                'js2-parse-eq-expr)))
+    pn))
+
+(defconst js2-parse-eq-ops
+  (list js2-EQ js2-NE js2-SHEQ js2-SHNE))
+
+(defun js2-parse-eq-expr ()
+  (let ((pn (js2-parse-rel-expr))
+        tt)
+    (while (memq (setq tt (js2-get-token)) js2-parse-eq-ops)
+      (setq pn (js2-make-binary tt
+                                pn
+                                'js2-parse-rel-expr)))
+    (js2-unget-token)
+    pn))
+
+(defconst js2-parse-rel-ops
+  (list js2-IN js2-INSTANCEOF js2-LE js2-LT js2-GE js2-GT))
+
+(defun js2-parse-rel-expr ()
+  (let ((pn (js2-parse-shift-expr))
+        (continue t)
+        tt)
+    (while continue
+      (setq tt (js2-get-token))
+      (cond
+       ((and js2-in-for-init (= tt js2-IN))
+        (js2-unget-token)
+        (setq continue nil))
+       ((memq tt js2-parse-rel-ops)
+        (setq pn (js2-make-binary tt pn 'js2-parse-shift-expr)))
+       (t
+        (js2-unget-token)
+        (setq continue nil))))
+    pn))
+
+(defconst js2-parse-shift-ops
+  (list js2-LSH js2-URSH js2-RSH))
+
+(defun js2-parse-shift-expr ()
+  (let ((pn (js2-parse-add-expr))
+        tt
+        (continue t))
+    (while continue
+      (setq tt (js2-get-token))
+      (if (memq tt js2-parse-shift-ops)
+          (setq pn (js2-make-binary tt pn 'js2-parse-add-expr))
+        (js2-unget-token)
+        (setq continue nil)))
+    pn))
+
+(defun js2-parse-add-expr ()
+  (let ((pn (js2-parse-mul-expr))
+        tt
+        (continue t))
+    (while continue
+      (setq tt (js2-get-token))
+      (if (or (= tt js2-ADD) (= tt js2-SUB))
+          (setq pn (js2-make-binary tt pn 'js2-parse-mul-expr))
+        (js2-unget-token)
+        (setq continue nil)))
+    pn))
+
+(defconst js2-parse-mul-ops
+  (list js2-MUL js2-DIV js2-MOD))
+
+(defun js2-parse-mul-expr ()
+  (let ((pn (js2-parse-unary-expr))
+        tt
+        (continue t))
+    (while continue
+      (setq tt (js2-get-token))
+      (if (memq tt js2-parse-mul-ops)
+          (setq pn (js2-make-binary tt pn 'js2-parse-unary-expr))
+        (js2-unget-token)
+        (setq continue nil)))
+    pn))
+
+(defun js2-make-unary (type parser &rest args)
+  "Make a unary node of type TYPE.
+PARSER is either a node (for postfix operators) or a function to call
+to parse the operand (for prefix operators)."
+  (let* ((pos (js2-current-token-beg))
+         (postfix (js2-node-p parser))
+         (expr (if postfix
+                   parser
+                 (apply parser args)))
+         end
+         pn)
+    (if postfix  ; e.g. i++
+        (setq pos (js2-node-pos expr)
+              end (js2-current-token-end))
+      (setq end (js2-node-end expr)))
+    (setq pn (make-js2-unary-node :type type
+                                  :pos pos
+                                  :len (- end pos)
+                                  :operand expr))
+    (js2-node-add-children pn expr)
+    pn))
+
+(defconst js2-incrementable-node-types
+  (list js2-NAME js2-GETPROP js2-GETELEM js2-GET_REF js2-CALL)
+  "Node types that can be the operand of a ++ or -- operator.")
+
+(defun js2-check-bad-inc-dec (tt beg end unary)
+  (unless (memq (js2-node-type (js2-unary-node-operand unary))
+                js2-incrementable-node-types)
+    (js2-report-error (if (= tt js2-INC)
+                          "msg.bad.incr"
+                        "msg.bad.decr")
+                      nil beg (- end beg))))
+
+(defun js2-parse-unary-expr ()
+  (let ((tt (js2-current-token-type))
+        pn expr beg end)
+    (cond
+     ((or (= tt js2-VOID)
+          (= tt js2-NOT)
+          (= tt js2-BITNOT)
+          (= tt js2-TYPEOF))
+      (js2-get-token)
+      (js2-make-unary tt 'js2-parse-unary-expr))
+     ((= tt js2-ADD)
+      (js2-get-token)
+      ;; Convert to special POS token in decompiler and parse tree
+      (js2-make-unary js2-POS 'js2-parse-unary-expr))
+     ((= tt js2-SUB)
+      (js2-get-token)
+      ;; Convert to special NEG token in decompiler and parse tree
+      (js2-make-unary js2-NEG 'js2-parse-unary-expr))
+     ((or (= tt js2-INC)
+          (= tt js2-DEC))
+      (js2-get-token)
+      (prog1
+          (setq beg (js2-current-token-beg)
+                end (js2-current-token-end)
+                expr (js2-make-unary tt 'js2-parse-member-expr t))
+        (js2-check-bad-inc-dec tt beg end expr)))
+     ((= tt js2-DELPROP)
+      (js2-get-token)
+      (js2-make-unary js2-DELPROP 'js2-parse-unary-expr))
+     ((= tt js2-ERROR)
+      (js2-get-token)
+      (make-js2-error-node))  ; try to continue
+     ((and (= tt js2-LT)
+           js2-compiler-xml-available)
+      ;; XML stream encountered in expression.
+      (js2-parse-member-expr-tail t (js2-parse-xml-initializer)))
+     (t
+      (setq pn (js2-parse-member-expr t)
+            ;; Don't look across a newline boundary for a postfix incop.
+            tt (js2-peek-token-or-eol))
+      (when (or (= tt js2-INC) (= tt js2-DEC))
+        (js2-get-token)
+        (setf expr pn
+              pn (js2-make-unary tt expr))
+        (js2-node-set-prop pn 'postfix t)
+        (js2-check-bad-inc-dec tt (js2-current-token-beg) (js2-current-token-end) pn))
+      pn))))
+
+(defun js2-parse-xml-initializer ()
+  "Parse an E4X XML initializer.
+I'm parsing it the way Rhino parses it, but without the tree-rewriting.
+Then I'll postprocess the result, depending on whether we're in IDE
+mode or codegen mode, and generate the appropriate rewritten AST.
+IDE mode uses a rich AST that models the XML structure.  Codegen mode
+just concatenates everything and makes a new XML or XMLList out of it."
+  (let ((tt (js2-get-first-xml-token))
+        pn-xml pn expr kids expr-pos
+        (continue t)
+        (first-token t))
+    (when (not (or (= tt js2-XML) (= tt js2-XMLEND)))
+      (js2-report-error "msg.syntax"))
+    (setq pn-xml (make-js2-xml-node))
+    (while continue
+      (if first-token
+          (setq first-token nil)
+        (setq tt (js2-get-next-xml-token)))
+      (cond
+       ;; js2-XML means we found a {expr} in the XML stream.
+       ;; The token string is the XML up to the left-curly.
+       ((= tt js2-XML)
+        (push (make-js2-string-node :pos (js2-current-token-beg)
+                                    :len (- js2-ts-cursor (js2-current-token-beg)))
+              kids)
+        (js2-must-match js2-LC "msg.syntax")
+        (setq expr-pos js2-ts-cursor
+              expr (if (eq (js2-peek-token) js2-RC)
+                       (make-js2-empty-expr-node :pos expr-pos)
+                     (js2-parse-expr)))
+        (js2-must-match js2-RC "msg.syntax")
+        (setq pn (make-js2-xml-js-expr-node :pos (js2-node-pos expr)
+                                            :len (js2-node-len expr)
+                                            :expr expr))
+        (js2-node-add-children pn expr)
+        (push pn kids))
+       ;; a js2-XMLEND token means we hit the final close-tag.
+       ((= tt js2-XMLEND)
+        (push (make-js2-string-node :pos (js2-current-token-beg)
+                                    :len (- js2-ts-cursor (js2-current-token-beg)))
+              kids)
+        (dolist (kid (nreverse kids))
+          (js2-block-node-push pn-xml kid))
+        (setf (js2-node-len pn-xml) (- js2-ts-cursor
+                                       (js2-node-pos pn-xml))
+              continue nil))
+       (t
+        (js2-report-error "msg.syntax")
+        (setq continue nil))))
+    pn-xml))
+
+
+(defun js2-parse-argument-list ()
+  "Parse an argument list and return it as a Lisp list of nodes.
+Returns the list in reverse order.  Consumes the right-paren token."
+  (let (result)
+    (unless (js2-match-token js2-RP)
+      (cl-loop do
+               (let ((tt (js2-get-token)))
+                 (if (= tt js2-YIELD)
+                     (js2-report-error "msg.yield.parenthesized"))
+                 (if (and (= tt js2-TRIPLEDOT)
+                          (>= js2-language-version 200))
+                     (push (js2-make-unary tt 'js2-parse-assign-expr) result)
+                   (js2-unget-token)
+                   (push (js2-parse-assign-expr) result)))
+               while
+               (js2-match-token js2-COMMA))
+      (js2-must-match js2-RP "msg.no.paren.arg")
+      result)))
+
+(defun js2-parse-member-expr (&optional allow-call-syntax)
+  (let ((tt (js2-current-token-type))
+        pn pos target args beg end init)
+    (if (/= tt js2-NEW)
+        (setq pn (js2-parse-primary-expr))
+      ;; parse a 'new' expression
+      (js2-get-token)
+      (setq pos (js2-current-token-beg)
+            beg pos
+            target (js2-parse-member-expr)
+            end (js2-node-end target)
+            pn (make-js2-new-node :pos pos
+                                  :target target
+                                  :len (- end pos)))
+      (js2-highlight-function-call (js2-current-token))
+      (js2-node-add-children pn target)
+      (when (js2-match-token js2-LP)
+        ;; Add the arguments to pn, if any are supplied.
+        (setf beg pos  ; start of "new" keyword
+              pos (js2-current-token-beg)
+              args (nreverse (js2-parse-argument-list))
+              (js2-new-node-args pn) args
+              end (js2-current-token-end)
+              (js2-new-node-lp pn) (- pos beg)
+              (js2-new-node-rp pn) (- end 1 beg))
+        (apply #'js2-node-add-children pn args))
+      (when (and js2-allow-rhino-new-expr-initializer
+                 (js2-match-token js2-LC))
+        (setf init (js2-parse-object-literal)
+              end (js2-node-end init)
+              (js2-new-node-initializer pn) init)
+        (js2-node-add-children pn init))
+        (setf (js2-node-len pn) (- end beg)))  ; end outer if
+    (js2-parse-member-expr-tail allow-call-syntax pn)))
+
+(defun js2-parse-member-expr-tail (allow-call-syntax pn)
+  "Parse a chain of property/array accesses or function calls.
+Includes parsing for E4X operators like `..' and `.@'.
+If ALLOW-CALL-SYNTAX is nil, stops when we encounter a left-paren.
+Returns an expression tree that includes PN, the parent node."
+  (let (tt
+        (continue t))
+    (while continue
+      (setq tt (js2-get-token))
+      (cond
+       ((or (= tt js2-DOT) (= tt js2-DOTDOT))
+        (setq pn (js2-parse-property-access tt pn)))
+       ((= tt js2-DOTQUERY)
+        (setq pn (js2-parse-dot-query pn)))
+       ((= tt js2-LB)
+        (setq pn (js2-parse-element-get pn)))
+       ((= tt js2-LP)
+        (js2-unget-token)
+        (if allow-call-syntax
+            (setq pn (js2-parse-function-call pn))
+          (setq continue nil)))
+       ((= tt js2-TEMPLATE_HEAD)
+        (setq pn (js2-parse-tagged-template pn (js2-parse-template-literal))))
+       ((= tt js2-NO_SUBS_TEMPLATE)
+        (setq pn (js2-parse-tagged-template pn (make-js2-string-node :type tt))))
+       (t
+        (js2-unget-token)
+        (setq continue nil))))
+    (if (>= js2-highlight-level 2)
+        (js2-parse-highlight-member-expr-node pn))
+    pn))
+
+(defun js2-parse-tagged-template (tag-node tpl-node)
+  "Parse tagged template expression."
+  (let* ((beg (js2-node-pos tag-node))
+         (pn (make-js2-tagged-template-node :beg beg
+                                            :len (- (js2-current-token-end) beg)
+                                            :tag tag-node
+                                            :template tpl-node)))
+    (js2-node-add-children pn tag-node tpl-node)
+    pn))
+
+(defun js2-parse-dot-query (pn)
+  "Parse a dot-query expression, e.g. foo.bar.(@name == 2)
+Last token parsed must be `js2-DOTQUERY'."
+  (let ((pos (js2-node-pos pn))
+        op-pos expr end)
+    (js2-must-have-xml)
+    (js2-set-requires-activation)
+    (setq op-pos (js2-current-token-beg)
+          expr (js2-parse-expr)
+          end (js2-node-end expr)
+          pn (make-js2-xml-dot-query-node :left pn
+                                          :pos pos
+                                          :op-pos op-pos
+                                          :right expr))
+    (js2-node-add-children pn
+                           (js2-xml-dot-query-node-left pn)
+                           (js2-xml-dot-query-node-right pn))
+    (if (js2-must-match js2-RP "msg.no.paren")
+        (setf (js2-xml-dot-query-node-rp pn) (js2-current-token-beg)
+              end (js2-current-token-end)))
+    (setf (js2-node-len pn) (- end pos))
+    pn))
+
+(defun js2-parse-element-get (pn)
+  "Parse an element-get expression, e.g. foo[bar].
+Last token parsed must be `js2-RB'."
+  (let ((lb (js2-current-token-beg))
+        (pos (js2-node-pos pn))
+        rb expr)
+    (setq expr (js2-parse-expr))
+    (if (js2-must-match js2-RB "msg.no.bracket.index")
+        (setq rb (js2-current-token-beg)))
+    (setq pn (make-js2-elem-get-node :target pn
+                                     :pos pos
+                                     :element expr
+                                     :lb (js2-relpos lb pos)
+                                     :rb (js2-relpos rb pos)
+                                     :len (- (js2-current-token-end) pos)))
+    (js2-node-add-children pn
+                           (js2-elem-get-node-target pn)
+                           (js2-elem-get-node-element pn))
+    pn))
+
+(defun js2-highlight-function-call (token)
+  (when (eq (js2-token-type token) js2-NAME)
+    (js2-record-face 'js2-function-call token)))
+
+(defun js2-parse-function-call (pn)
+  (js2-highlight-function-call (js2-current-token))
+  (js2-get-token)
+  (let (args
+        (pos (js2-node-pos pn)))
+    (setq pn (make-js2-call-node :pos pos
+                                 :target pn
+                                 :lp (- (js2-current-token-beg) pos)))
+    (js2-node-add-children pn (js2-call-node-target pn))
+    ;; Add the arguments to pn, if any are supplied.
+    (setf args (nreverse (js2-parse-argument-list))
+          (js2-call-node-rp pn) (- (js2-current-token-beg) pos)
+          (js2-call-node-args pn) args)
+    (apply #'js2-node-add-children pn args)
+    (setf (js2-node-len pn) (- js2-ts-cursor pos))
+    pn))
+
+(defun js2-parse-property-access (tt pn)
+  "Parse a property access, XML descendants access, or XML attr access."
+  (let ((member-type-flags 0)
+        (dot-pos (js2-current-token-beg))
+        (dot-len (if (= tt js2-DOTDOT) 2 1))
+        name
+        ref  ; right side of . or .. operator
+        result)
+    (when (= tt js2-DOTDOT)
+      (js2-must-have-xml)
+      (setq member-type-flags js2-descendants-flag))
+    (if (not js2-compiler-xml-available)
+        (progn
+          (js2-must-match-prop-name "msg.no.name.after.dot")
+          (setq name (js2-create-name-node t js2-GETPROP)
+                result (make-js2-prop-get-node :left pn
+                                               :pos (js2-current-token-beg)
+                                               :right name
+                                               :len (js2-current-token-len)))
+          (js2-node-add-children result pn name)
+          result)
+      ;; otherwise look for XML operators
+      (setf result (if (= tt js2-DOT)
+                       (make-js2-prop-get-node)
+                     (make-js2-infix-node :type js2-DOTDOT))
+            (js2-node-pos result) (js2-node-pos pn)
+            (js2-infix-node-op-pos result) dot-pos
+            (js2-infix-node-left result) pn  ; do this after setting position
+            tt (js2-get-prop-name-token))
+      (cond
+       ;; handles: name, ns::name, ns::*, ns::[expr]
+       ((= tt js2-NAME)
+        (setq ref (js2-parse-property-name -1 nil member-type-flags)))
+       ;; handles: *, *::name, *::*, *::[expr]
+       ((= tt js2-MUL)
+        (setq ref (js2-parse-property-name nil "*" member-type-flags)))
+       ;; handles: '@attr', '@ns::attr', '@ns::*', '@ns::[expr]', etc.
+       ((= tt js2-XMLATTR)
+        (setq result (js2-parse-attribute-access)))
+       (t
+        (js2-report-error "msg.no.name.after.dot" nil dot-pos dot-len)))
+      (if ref
+          (setf (js2-node-len result) (- (js2-node-end ref)
+                                         (js2-node-pos result))
+                (js2-infix-node-right result) ref))
+      (if (js2-infix-node-p result)
+          (js2-node-add-children result
+                                 (js2-infix-node-left result)
+                                 (js2-infix-node-right result)))
+      result)))
+
+(defun js2-parse-attribute-access ()
+  "Parse an E4X XML attribute expression.
+This includes expressions of the forms:
+
+  @attr      @ns::attr     @ns::*
+  @*         @*::attr      @*::*
+  @[expr]    @*::[expr]    @ns::[expr]
+
+Called if we peeked an '@' token."
+  (let ((tt (js2-get-prop-name-token))
+        (at-pos (js2-current-token-beg)))
+    (cond
+     ;; handles: @name, @ns::name, @ns::*, @ns::[expr]
+     ((= tt js2-NAME)
+      (js2-parse-property-name at-pos nil 0))
+     ;; handles: @*, @*::name, @*::*, @*::[expr]
+     ((= tt js2-MUL)
+      (js2-parse-property-name (js2-current-token-beg) "*" 0))
+     ;; handles @[expr]
+     ((= tt js2-LB)
+      (js2-parse-xml-elem-ref at-pos))
+     (t
+      (js2-report-error "msg.no.name.after.xmlAttr")
+      ;; Avoid cascaded errors that happen if we make an error node here.
+      (js2-parse-property-name (js2-current-token-beg) "" 0)))))
+
+(defun js2-parse-property-name (at-pos s member-type-flags)
+  "Check if :: follows name in which case it becomes qualified name.
+
+AT-POS is a natural number if we just read an '@' token, else nil.
+S is the name or string that was matched:  an identifier, 'throw' or '*'.
+MEMBER-TYPE-FLAGS is a bit set tracking whether we're a '.' or '..' child.
+
+Returns a `js2-xml-ref-node' if it's an attribute access, a child of a '..'
+operator, or the name is followed by ::.  For a plain name, returns a
+`js2-name-node'.  Returns a `js2-error-node' for malformed XML expressions."
+  (let ((pos (or at-pos (js2-current-token-beg)))
+        colon-pos
+        (name (js2-create-name-node t (js2-current-token-type) s))
+        ns tt pn)
+    (catch 'return
+      (when (js2-match-token js2-COLONCOLON)
+        (setq ns name
+              colon-pos (js2-current-token-beg)
+              tt (js2-get-prop-name-token))
+        (cond
+         ;; handles name::name
+         ((= tt js2-NAME)
+          (setq name (js2-create-name-node)))
+         ;; handles name::*
+         ((= tt js2-MUL)
+          (setq name (js2-create-name-node nil nil "*")))
+         ;; handles name::[expr]
+         ((= tt js2-LB)
+          (throw 'return (js2-parse-xml-elem-ref at-pos ns colon-pos)))
+         (t
+          (js2-report-error "msg.no.name.after.coloncolon"))))
+      (if (and (null ns) (zerop member-type-flags))
+          name
+        (prog1
+            (setq pn
+                  (make-js2-xml-prop-ref-node :pos pos
+                                              :len (- (js2-node-end name) pos)
+                                              :at-pos at-pos
+                                              :colon-pos colon-pos
+                                              :propname name))
+          (js2-node-add-children pn name))))))
+
+(defun js2-parse-xml-elem-ref (at-pos &optional namespace colon-pos)
+  "Parse the [expr] portion of an xml element reference.
+For instance, @[expr], @*::[expr], or ns::[expr]."
+  (let* ((lb (js2-current-token-beg))
+         (pos (or at-pos lb))
+         rb
+         (expr (js2-parse-expr))
+         (end (js2-node-end expr))
+         pn)
+    (if (js2-must-match js2-RB "msg.no.bracket.index")
+        (setq rb (js2-current-token-beg)
+              end (js2-current-token-end)))
+    (prog1
+        (setq pn
+              (make-js2-xml-elem-ref-node :pos pos
+                                          :len (- end pos)
+                                          :namespace namespace
+                                          :colon-pos colon-pos
+                                          :at-pos at-pos
+                                          :expr expr
+                                          :lb (js2-relpos lb pos)
+                                          :rb (js2-relpos rb pos)))
+      (js2-node-add-children pn namespace expr))))
+
+(defun js2-parse-destruct-primary-expr ()
+  (let ((js2-is-in-destructuring t))
+    (js2-parse-primary-expr)))
+
+(defun js2-parse-primary-expr ()
+  "Parse a literal (leaf) expression of some sort.
+Includes complex literals such as functions, object-literals,
+array-literals, array comprehensions and regular expressions."
+  (let (pn      ; parent node  (usually return value)
+        tt)
+    (setq tt (js2-current-token-type))
+    (cond
+     ((= tt js2-CLASS)
+      (js2-parse-class-expr))
+     ((= tt js2-FUNCTION)
+      (js2-parse-function-expr))
+     ((= tt js2-LB)
+      (js2-parse-array-comp-or-literal))
+     ((= tt js2-LC)
+      (js2-parse-object-literal))
+     ((= tt js2-LET)
+      (js2-parse-let (js2-current-token-beg)))
+     ((= tt js2-LP)
+      (js2-parse-paren-expr-or-generator-comp))
+     ((= tt js2-XMLATTR)
+      (js2-must-have-xml)
+      (js2-parse-attribute-access))
+     ((= tt js2-NAME)
+      (js2-parse-name tt))
+     ((= tt js2-NUMBER)
+      (make-js2-number-node))
+     ((or (= tt js2-STRING) (= tt js2-NO_SUBS_TEMPLATE))
+      (make-js2-string-node :type tt))
+     ((= tt js2-TEMPLATE_HEAD)
+      (js2-parse-template-literal))
+     ((or (= tt js2-DIV) (= tt js2-ASSIGN_DIV))
+      ;; Got / or /= which in this context means a regexp literal
+      (let ((px-pos (js2-current-token-beg))
+            (flags (js2-read-regexp tt))
+            (end (js2-current-token-end)))
+        (prog1
+            (make-js2-regexp-node :pos px-pos
+                                  :len (- end px-pos)
+                                  :value (js2-current-token-string)
+                                  :flags flags)
+          (js2-set-face px-pos end 'font-lock-string-face 'record)
+          (js2-record-text-property px-pos end 'syntax-table '(2)))))
+     ((or (= tt js2-NULL)
+          (= tt js2-THIS)
+          (= tt js2-SUPER)
+          (= tt js2-FALSE)
+          (= tt js2-TRUE))
+      (make-js2-keyword-node :type tt))
+     ((= tt js2-RP)
+      ;; Not valid expression syntax, but this is valid in an arrow
+      ;; function with no params: () => body.
+      (if (eq (js2-peek-token) js2-ARROW)
+          (progn
+            (js2-unget-token)  ; Put back the right paren.
+            ;; Return whatever, it will hopefully be rewinded and
+            ;; reparsed when we reach the =>.
+            (make-js2-keyword-node :type js2-NULL))
+        (js2-report-error "msg.syntax")
+        (make-js2-error-node)))
+     ((= tt js2-TRIPLEDOT)
+      ;; Likewise, only valid in an arrow function with a rest param.
+      (if (and (js2-match-token js2-NAME)
+               (js2-match-token js2-RP)
+               (eq (js2-peek-token) js2-ARROW))
+          (progn
+            (js2-unget-token)  ; Put back the right paren.
+            ;; See the previous case.
+            (make-js2-keyword-node :type js2-NULL))
+        (js2-report-error "msg.syntax")
+        (make-js2-error-node)))
+     ((= tt js2-RESERVED)
+      (js2-report-error "msg.reserved.id")
+      (make-js2-name-node))
+     ((= tt js2-ERROR)
+      ;; the scanner or one of its subroutines reported the error.
+      (make-js2-error-node))
+     ((= tt js2-EOF)
+      (let* ((px-pos (point-at-bol))
+             (len (- js2-ts-cursor px-pos)))
+        (js2-report-error "msg.unexpected.eof" nil px-pos len))
+      (make-js2-error-node :pos (1- js2-ts-cursor)))
+     (t
+      (js2-report-error "msg.syntax")
+      (make-js2-error-node)))))
+
+(defun js2-parse-template-literal ()
+  (let ((beg (js2-current-token-beg))
+        (kids (list (make-js2-string-node :type js2-TEMPLATE_HEAD)))
+        (tt js2-TEMPLATE_HEAD))
+    (while (eq tt js2-TEMPLATE_HEAD)
+      (push (js2-parse-expr) kids)
+      (js2-must-match js2-RC "msg.syntax")
+      (setq tt (js2-get-token 'TEMPLATE_TAIL))
+      (push (make-js2-string-node :type tt) kids))
+    (setq kids (nreverse kids))
+    (let ((tpl (make-js2-template-node :beg beg
+                                       :len (- (js2-current-token-end) beg)
+                                       :kids kids)))
+      (apply #'js2-node-add-children tpl kids)
+      tpl)))
+
+(defun js2-parse-name (_tt)
+  (let ((name (js2-current-token-string))
+        node)
+    (setq node (if js2-compiler-xml-available
+                   (js2-parse-property-name nil name 0)
+                 (js2-create-name-node 'check-activation nil name)))
+    (if js2-highlight-external-variables
+        (js2-record-name-node node))
+    node))
+
+(defun js2-parse-warn-trailing-comma (msg pos elems comma-pos)
+  (js2-add-strict-warning
+   msg nil
+   ;; back up from comma to beginning of line or array/objlit
+   (max (if elems
+            (js2-node-pos (car elems))
+          pos)
+        (save-excursion
+          (goto-char comma-pos)
+          (back-to-indentation)
+          (point)))
+   comma-pos))
+
+(defun js2-parse-array-comp-or-literal ()
+  (let ((pos (js2-current-token-beg)))
+    (if (and (>= js2-language-version 200)
+             (js2-match-token js2-FOR))
+        (js2-parse-array-comp pos)
+      (js2-parse-array-literal pos))))
+
+(defun js2-parse-array-literal (pos)
+  (let ((after-lb-or-comma t)
+        after-comma tt elems pn
+        (continue t))
+    (unless js2-is-in-destructuring
+      (js2-push-scope (make-js2-scope))) ; for the legacy array comp
+    (while continue
+      (setq tt (js2-get-token))
+      (cond
+       ;; comma
+       ((= tt js2-COMMA)
+        (setq after-comma (js2-current-token-end))
+        (if (not after-lb-or-comma)
+            (setq after-lb-or-comma t)
+          (push nil elems)))
+       ;; end of array
+       ((or (= tt js2-RB)
+            (= tt js2-EOF))  ; prevent infinite loop
+        (if (= tt js2-EOF)
+            (js2-report-error "msg.no.bracket.arg" nil pos))
+        (when (and after-comma (< js2-language-version 170))
+          (js2-parse-warn-trailing-comma "msg.array.trailing.comma"
+                                         pos (remove nil elems) after-comma))
+        (setq continue nil
+              pn (make-js2-array-node :pos pos
+                                      :len (- js2-ts-cursor pos)
+                                      :elems (nreverse elems)))
+        (apply #'js2-node-add-children pn (js2-array-node-elems pn)))
+       ;; destructuring binding
+       (js2-is-in-destructuring
+        (push (if (or (= tt js2-LC)
+                      (= tt js2-LB)
+                      (= tt js2-NAME))
+                  ;; [a, b, c] | {a, b, c} | {a:x, b:y, c:z} | a
+                  (js2-parse-destruct-primary-expr)
+                ;; invalid pattern
+                (js2-report-error "msg.bad.var")
+                (make-js2-error-node))
+              elems)
+        (setq after-lb-or-comma nil
+              after-comma nil))
+       ;; array comp
+       ((and (>= js2-language-version 170)
+             (= tt js2-FOR)          ; check for array comprehension
+             (not after-lb-or-comma) ; "for" can't follow a comma
+             elems                   ; must have at least 1 element
+             (not (cdr elems)))      ; but no 2nd element
+        (js2-unget-token)
+        (setf continue nil
+              pn (js2-parse-legacy-array-comp (car elems) pos)))
+       ;; another element
+       (t
+        (unless after-lb-or-comma
+          (js2-report-error "msg.no.bracket.arg"))
+        (if (and (= tt js2-TRIPLEDOT)
+                 (>= js2-language-version 200))
+            ;; spread operator
+            (push (js2-make-unary tt 'js2-parse-assign-expr)
+                  elems)
+          (js2-unget-token)
+          (push (js2-parse-assign-expr) elems))
+        (setq after-lb-or-comma nil
+              after-comma nil))))
+    (unless js2-is-in-destructuring
+      (js2-pop-scope))
+    pn))
+
+(defun js2-parse-legacy-array-comp (expr pos)
+  "Parse a legacy array comprehension (JavaScript 1.7).
+EXPR is the first expression after the opening left-bracket.
+POS is the beginning of the LB token preceding EXPR.
+We should have just parsed the 'for' keyword before calling this function."
+  (let ((current-scope js2-current-scope)
+        loops first filter result)
+    (unwind-protect
+        (progn
+          (while (js2-match-token js2-FOR)
+            (let ((loop (make-js2-comp-loop-node)))
+              (js2-push-scope loop)
+              (push loop loops)
+              (js2-parse-comp-loop loop)))
+          ;; First loop takes expr scope's parent.
+          (setf (js2-scope-parent-scope (setq first (car (last loops))))
+                (js2-scope-parent-scope current-scope))
+          ;; Set expr scope's parent to the last loop.
+          (setf (js2-scope-parent-scope current-scope) (car loops))
+          (if (/= (js2-get-token) js2-IF)
+              (js2-unget-token)
+            (setq filter (js2-parse-condition))))
+      (dotimes (_ (1- (length loops)))
+        (js2-pop-scope)))
+    (js2-must-match js2-RB "msg.no.bracket.arg" pos)
+    (setq result (make-js2-comp-node :pos pos
+                                     :len (- js2-ts-cursor pos)
+                                     :result expr
+                                     :loops (nreverse loops)
+                                     :filters (and filter (list (car filter)))
+                                     :form 'LEGACY_ARRAY))
+    (apply #'js2-node-add-children result expr (car filter)
+           (js2-comp-node-loops result))
+    result))
+
+(defun js2-parse-array-comp (pos)
+  "Parse an ES6 array comprehension.
+POS is the beginning of the LB token.
+We should have just parsed the 'for' keyword before calling this function."
+  (let ((pn (js2-parse-comprehension pos 'ARRAY)))
+    (js2-must-match js2-RB "msg.no.bracket.arg" pos)
+    pn))
+
+(defun js2-parse-generator-comp (pos)
+  (let* ((js2-nesting-of-function (1+ js2-nesting-of-function))
+         (js2-current-script-or-fn
+          (make-js2-function-node :generator-type 'COMPREHENSION))
+         (pn (js2-parse-comprehension pos 'STAR_GENERATOR)))
+    (js2-must-match js2-RP "msg.no.paren" pos)
+    pn))
+
+(defun js2-parse-comprehension (pos form)
+  (let (loops filters expr result)
+    (unwind-protect
+        (progn
+          (js2-unget-token)
+          (while (js2-match-token js2-FOR)
+            (let ((loop (make-js2-comp-loop-node)))
+              (js2-push-scope loop)
+              (push loop loops)
+              (js2-parse-comp-loop loop)))
+          (while (js2-match-token js2-IF)
+            (push (car (js2-parse-condition)) filters))
+          (setq expr (js2-parse-assign-expr)))
+      (dolist (_ loops)
+        (js2-pop-scope)))
+    (setq result (make-js2-comp-node :pos pos
+                                     :len (- js2-ts-cursor pos)
+                                     :result expr
+                                     :loops (nreverse loops)
+                                     :filters (nreverse filters)
+                                     :form form))
+    (apply #'js2-node-add-children result (js2-comp-node-loops result))
+    (apply #'js2-node-add-children result expr (js2-comp-node-filters result))
+    result))
+
+(defun js2-parse-comp-loop (pn &optional only-of-p)
+  "Parse a 'for [each] (foo [in|of] bar)' expression in an Array comprehension.
+The current token should be the initial FOR.
+If ONLY-OF-P is non-nil, only the 'for (foo of bar)' form is allowed."
+  (let ((pos (js2-comp-loop-node-pos pn))
+        tt iter obj foreach-p forof-p in-pos each-pos lp rp)
+    (when (and (not only-of-p) (js2-match-token js2-NAME))
+      (if (string= (js2-current-token-string) "each")
+          (progn
+            (setq foreach-p t
+                  each-pos (- (js2-current-token-beg) pos)) ; relative
+            (js2-record-face 'font-lock-keyword-face))
+        (js2-report-error "msg.no.paren.for")))
+    (if (js2-must-match js2-LP "msg.no.paren.for")
+        (setq lp (- (js2-current-token-beg) pos)))
+    (setq tt (js2-peek-token))
+    (cond
+     ((or (= tt js2-LB)
+          (= tt js2-LC))
+      (js2-get-token)
+      (setq iter (js2-parse-destruct-primary-expr))
+      (js2-define-destruct-symbols iter js2-LET
+                                   'font-lock-variable-name-face t))
+     ((js2-match-token js2-NAME)
+      (setq iter (js2-create-name-node)))
+     (t
+      (js2-report-error "msg.bad.var")))
+    ;; Define as a let since we want the scope of the variable to
+    ;; be restricted to the array comprehension
+    (if (js2-name-node-p iter)
+        (js2-define-symbol js2-LET (js2-name-node-name iter) pn t))
+    (if (or (and (not only-of-p) (js2-match-token js2-IN))
+            (and (>= js2-language-version 200)
+                 (js2-match-contextual-kwd "of")
+                 (setq forof-p t)))
+        (setq in-pos (- (js2-current-token-beg) pos))
+      (js2-report-error "msg.in.after.for.name"))
+    (setq obj (js2-parse-expr))
+    (if (js2-must-match js2-RP "msg.no.paren.for.ctrl")
+        (setq rp (- (js2-current-token-beg) pos)))
+    (setf (js2-node-pos pn) pos
+          (js2-node-len pn) (- js2-ts-cursor pos)
+          (js2-comp-loop-node-iterator pn) iter
+          (js2-comp-loop-node-object pn) obj
+          (js2-comp-loop-node-in-pos pn) in-pos
+          (js2-comp-loop-node-each-pos pn) each-pos
+          (js2-comp-loop-node-foreach-p pn) foreach-p
+          (js2-comp-loop-node-forof-p pn) forof-p
+          (js2-comp-loop-node-lp pn) lp
+          (js2-comp-loop-node-rp pn) rp)
+    (js2-node-add-children pn iter obj)
+    pn))
+
+(defun js2-parse-class-stmt ()
+  (let ((pos (js2-current-token-beg)))
+    (js2-must-match-name "msg.unnamed.class.stmt")
+    (js2-parse-class pos 'CLASS_STATEMENT (js2-create-name-node t))))
+
+(defun js2-parse-class-expr ()
+  (let ((pos (js2-current-token-beg))
+        name)
+    (when (js2-match-token js2-NAME)
+      (setq name (js2-create-name-node t)))
+    (js2-parse-class pos 'CLASS_EXPRESSION name)))
+
+(defun js2-parse-class (pos form name)
+  ;; class X [extends ...] {
+  (let (pn elems extends)
+    (when name
+      (js2-set-face (js2-node-pos name) (js2-node-end name)
+                    'font-lock-function-name-face 'record))
+    (if (js2-match-token js2-EXTENDS)
+        (if (= (js2-peek-token) js2-LC)
+            (js2-report-error "msg.missing.extends")
+          ;; TODO(sdh): this should be left-hand-side-expr, not assign-expr
+          (setq extends (js2-parse-assign-expr))
+          (if (not extends)
+              (js2-report-error "msg.bad.extends"))))
+    (js2-must-match js2-LC "msg.no.brace.class")
+    (setq elems (js2-parse-object-literal-elems t)
+          pn (make-js2-class-node :pos pos
+                                  :len (- js2-ts-cursor pos)
+                                  :form form
+                                  :name name
+                                  :extends extends
+                                  :elems elems))
+    (apply #'js2-node-add-children pn (js2-class-node-elems pn))
+    pn))
+
+(defun js2-parse-object-literal ()
+  (let* ((pos (js2-current-token-beg))
+         (elems (js2-parse-object-literal-elems))
+         (result (make-js2-object-node :pos pos
+                                       :len (- js2-ts-cursor pos)
+                                       :elems elems)))
+    (apply #'js2-node-add-children result (js2-object-node-elems result))
+    result))
+
+(defun js2-parse-object-literal-elems (&optional class-p)
+  (let ((pos (js2-current-token-beg))
+        (static nil)
+        (continue t)
+        tt elems elem after-comma)
+    (while continue
+      (setq static (and class-p (js2-match-token js2-STATIC))
+            tt (js2-get-prop-name-token)
+            elem nil)
+      (cond
+       ;; {foo: ...}, {'foo': ...}, {foo, bar, ...},
+       ;; {get foo() {...}}, {set foo(x) {...}}, or {foo(x) {...}}
+       ;; TODO(sdh): support *foo() {...}
+       ((or (= js2-NAME tt)
+            (= tt js2-STRING))
+        (setq after-comma nil
+              elem (js2-parse-named-prop tt))
+        (if (and (null elem)
+                 (not js2-recover-from-parse-errors))
+            (setq continue nil)))
+       ;; {[Symbol.iterator]: ...}
+       ((and (= tt js2-LB)
+             (>= js2-language-version 200))
+        (let ((expr (js2-parse-expr)))
+          (js2-must-match js2-RB "msg.missing.computed.rb")
+          (setq after-comma nil
+                elem (js2-parse-plain-property expr))))
+       ;; {12: x} or {10.7: x}
+       ((= tt js2-NUMBER)
+        (setq after-comma nil
+              elem (js2-parse-plain-property (make-js2-number-node))))
+       ;; Break out of loop, and handle trailing commas.
+       ((or (= tt js2-RC)
+            (= tt js2-EOF))
+        (js2-unget-token)
+        (setq continue nil)
+        (if after-comma
+            (js2-parse-warn-trailing-comma "msg.extra.trailing.comma"
+                                           pos elems after-comma)))
+       (t
+        (js2-report-error "msg.bad.prop")
+        (unless js2-recover-from-parse-errors
+          (setq continue nil))))         ; end switch
+      ;; Handle static for classes' codegen.
+      (if static
+          (if elem (js2-node-set-prop elem 'STATIC t)
+            (js2-report-error "msg.unexpected.static")))
+      ;; Handle commas, depending on class-p.
+      (let ((comma (js2-match-token js2-COMMA)))
+        (if class-p
+            (if comma
+                (js2-report-error "msg.class.unexpected.comma"))
+          (if comma
+              (setq after-comma (js2-current-token-end))
+            (setq continue nil))))
+      ;; Append any parsed element.
+      (if elem (push elem elems)))       ; end loop
+    (js2-must-match js2-RC "msg.no.brace.prop")
+    (nreverse elems)))
+
+(defun js2-parse-named-prop (tt)
+  "Parse a name, string, or getter/setter object property.
+When `js2-is-in-destructuring' is t, forms like {a, b, c} will be permitted."
+  (let ((string-prop (and (= tt js2-STRING)
+                          (make-js2-string-node)))
+        expr
+        (ppos (js2-current-token-beg))
+        (pend (js2-current-token-end))
+        (name (js2-create-name-node))
+        (prop (js2-current-token-string)))
+    (cond
+     ;; getter/setter prop
+     ((and (= tt js2-NAME)
+           (= (js2-peek-token) js2-NAME)
+           (or (string= prop "get")
+               (string= prop "set")))
+      (js2-get-token)
+      (js2-set-face ppos pend 'font-lock-keyword-face 'record)  ; get/set
+      (js2-record-face 'font-lock-function-name-face)      ; for peeked name
+      (setq name (js2-create-name-node)) ; discard get/set & use peeked name
+      (js2-parse-getter-setter-prop ppos name prop))
+     ;; method definition: {f() {...}}
+     ((and (= (js2-peek-token) js2-LP)
+           (>= js2-language-version 200))
+      (js2-record-face 'font-lock-function-name-face)      ; name
+      (js2-parse-getter-setter-prop ppos name ""))
+     ;; regular prop
+     (t
+      (prog1
+          (setq expr (js2-parse-plain-property (or string-prop name)))
+        (when (and (not string-prop)
+                   (not js2-is-in-destructuring)
+                   js2-highlight-external-variables
+                   (js2-node-get-prop expr 'SHORTHAND))
+          (js2-record-name-node name))
+        (js2-set-face ppos pend
+                      (if (js2-function-node-p
+                           (js2-object-prop-node-right expr))
+                          'font-lock-function-name-face
+                        'font-lock-variable-name-face)
+                      'record))))))
+
+(defun js2-parse-plain-property (prop)
+  "Parse a non-getter/setter property in an object literal.
+PROP is the node representing the property:  a number, name or string."
+  (let* ((tt (js2-get-token))
+         (pos (js2-node-pos prop))
+         colon expr result)
+    (cond
+     ;; Abbreviated property, as in {foo, bar}
+     ((and (>= js2-language-version 200)
+           (or (= tt js2-COMMA)
+               (= tt js2-RC))
+           (not (js2-number-node-p prop)))
+      (js2-unget-token)
+      (setq result (make-js2-object-prop-node
+                    :pos pos
+                    :left prop
+                    :right prop
+                    :op-pos (js2-current-token-len)))
+      (js2-node-add-children result prop)
+      (js2-node-set-prop result 'SHORTHAND t)
+      result)
+     ;; Normal property
+     (t
+      (if (= tt js2-COLON)
+          (setq colon (- (js2-current-token-beg) pos)
+                expr (js2-parse-assign-expr))
+        (js2-report-error "msg.no.colon.prop")
+        (setq expr (make-js2-error-node)))
+      (setq result (make-js2-object-prop-node
+                   :pos pos
+                   ;; don't include last consumed token in length
+                   :len (- (+ (js2-node-pos expr)
+                              (js2-node-len expr))
+                           pos)
+                   :left prop
+                   :right expr
+                   :op-pos colon))
+      (js2-node-add-children result prop expr)
+      result))))
+
+(defun js2-parse-getter-setter-prop (pos prop type-string)
+  "Parse getter or setter property in an object literal.
+JavaScript syntax is:
+
+  { get foo() {...}, set foo(x) {...} }
+
+and expression closure style is also supported
+
+  { get foo() x, set foo(x) _x = x }
+
+POS is the start position of the `get' or `set' keyword.
+PROP is the `js2-name-node' representing the property name.
+GET-P is non-nil if the keyword was `get'."
+  (let ((type (cond
+               ((string= "get" type-string) js2-GET)
+               ((string= "set" type-string) js2-SET)
+               (t js2-FUNCTION)))
+        result end
+        (fn (js2-parse-function-expr)))
+    ;; it has to be an anonymous function, as we already parsed the name
+    (if (/= (js2-node-type fn) js2-FUNCTION)
+        (js2-report-error "msg.bad.prop")
+      (if (cl-plusp (length (js2-function-name fn)))
+          (js2-report-error "msg.bad.prop")))
+    (js2-node-set-prop fn 'GETTER_SETTER type)  ; for codegen
+    (setq end (js2-node-end fn)
+          result (make-js2-getter-setter-node :type type
+                                              :pos pos
+                                              :len (- end pos)
+                                              :left prop
+                                              :right fn))
+    (js2-node-add-children result prop fn)
+    result))
+
+(defun js2-create-name-node (&optional check-activation-p token string)
+  "Create a name node using the current token and, optionally, STRING.
+And, if CHECK-ACTIVATION-P is non-nil, use the value of TOKEN."
+  (let* ((beg (js2-current-token-beg))
+         (tt (js2-current-token-type))
+         (s (or string
+                (if (= js2-NAME tt)
+                    (js2-current-token-string)
+                  (js2-tt-name tt))))
+         name)
+    (setq name (make-js2-name-node :pos beg
+                                   :name s
+                                   :len (length s)))
+    (if check-activation-p
+        (js2-check-activation-name s (or token js2-NAME)))
+    name))
+
+;;; Indentation support
+
+;; This indenter is based on Karl Landström's "javascript.el" indenter.
+;; Karl cleverly deduces that the desired indentation level is often a
+;; function of paren/bracket/brace nesting depth, which can be determined
+;; quickly via the built-in `parse-partial-sexp' function.  His indenter
+;; then does some equally clever checks to see if we're in the context of a
+;; substatement of a possibly braceless statement keyword such as if, while,
+;; or finally.  This approach yields pretty good results.
+
+;; The indenter is often "wrong", however, and needs to be overridden.
+;; The right long-term solution is probably to emulate (or integrate
+;; with) cc-engine, but it's a nontrivial amount of coding.  Even when a
+;; parse tree from `js2-parse' is present, which is not true at the
+;; moment the user is typing, computing indentation is still thousands
+;; of lines of code to handle every possible syntactic edge case.
+
+;; In the meantime, the compromise solution is that we offer a "bounce
+;; indenter", configured with `js2-bounce-indent-p', which cycles the
+;; current line indent among various likely guess points.  This approach
+;; is far from perfect, but should at least make it slightly easier to
+;; move the line towards its desired indentation when manually
+;; overriding Karl's heuristic nesting guesser.
+
+;; I've made miscellaneous tweaks to Karl's code to handle some Ecma
+;; extensions such as `let' and Array comprehensions.  Major kudos to
+;; Karl for coming up with the initial approach, which packs a lot of
+;; punch for so little code.
+
+(defconst js2-possibly-braceless-keywords-re
+  (concat "else[ \t]+if\\|for[ \t]+each\\|"
+          (regexp-opt '("catch" "do" "else" "finally" "for" "if"
+                        "try" "while" "with" "let")))
+  "Regular expression matching keywords that are optionally
+followed by an opening brace.")
+
+(defconst js2-indent-operator-re
+  (concat "[-+*/%<>&^|?:.]\\([^-+*/]\\|$\\)\\|!?=\\|"
+          (regexp-opt '("in" "instanceof") 'words))
+  "Regular expression matching operators that affect indentation
+of continued expressions.")
+
+(defconst js2-declaration-keyword-re
+  (regexp-opt '("var" "let" "const") 'words)
+  "Regular expression matching variable declaration keywords.")
+
+(defun js2-re-search-forward-inner (regexp &optional bound count)
+  "Auxiliary function for `js2-re-search-forward'."
+  (let (parse saved-point)
+    (while (> count 0)
+      (re-search-forward regexp bound)
+      (setq parse (if saved-point
+                      (parse-partial-sexp saved-point (point))
+                    (syntax-ppss (point))))
+      (cond ((nth 3 parse)
+             (re-search-forward
+              (concat "\\(\\=\\|[^\\]\\|^\\)" (string (nth 3 parse)))
+              (save-excursion (end-of-line) (point)) t))
+            ((nth 7 parse)
+             (forward-line))
+            ((or (nth 4 parse)
+                 (and (eq (char-before) ?\/) (eq (char-after) ?\*)))
+             (re-search-forward "\\*/"))
+            (t
+             (setq count (1- count))))
+      (setq saved-point (point))))
+  (point))
+
+(defun js2-re-search-forward (regexp &optional bound noerror count)
+  "Search forward but ignore strings and comments.
+Invokes `re-search-forward' but treats the buffer as if strings
+and comments have been removed."
+  (let ((saved-point (point)))
+    (condition-case err
+        (cond ((null count)
+               (js2-re-search-forward-inner regexp bound 1))
+              ((< count 0)
+               (js2-re-search-backward-inner regexp bound (- count)))
+              ((> count 0)
+               (js2-re-search-forward-inner regexp bound count)))
+      (search-failed
+       (goto-char saved-point)
+       (unless noerror
+         (error (error-message-string err)))))))
+
+(defun js2-re-search-backward-inner (regexp &optional bound count)
+  "Auxiliary function for `js2-re-search-backward'."
+  (let (parse)
+    (while (> count 0)
+      (re-search-backward regexp bound)
+      (setq parse (syntax-ppss (point)))
+      (cond ((nth 3 parse)
+             (re-search-backward
+              (concat "\\([^\\]\\|^\\)" (string (nth 3 parse)))
+              (line-beginning-position) t))
+            ((nth 7 parse)
+             (goto-char (nth 8 parse)))
+            ((or (nth 4 parse)
+                 (and (eq (char-before) ?/) (eq (char-after) ?*)))
+             (re-search-backward "/\\*"))
+            (t
+             (setq count (1- count))))))
+  (point))
+
+(defun js2-re-search-backward (regexp &optional bound noerror count)
+  "Search backward but ignore strings and comments.
+Invokes `re-search-backward' but treats the buffer as if strings
+and comments have been removed."
+  (let ((saved-point (point)))
+    (condition-case err
+        (cond ((null count)
+               (js2-re-search-backward-inner regexp bound 1))
+              ((< count 0)
+               (js2-re-search-forward-inner regexp bound (- count)))
+              ((> count 0)
+               (js2-re-search-backward-inner regexp bound count)))
+      (search-failed
+       (goto-char saved-point)
+       (unless noerror
+         (error (error-message-string err)))))))
+
+(defun js2-looking-at-operator-p ()
+  "Return non-nil if text after point is a non-comma operator."
+  (and (looking-at js2-indent-operator-re)
+       (or (not (looking-at ":"))
+           (save-excursion
+             (and (js2-re-search-backward "[?:{]\\|\\<case\\>" nil t)
+                  (looking-at "?"))))))
+
+(defun js2-continued-expression-p ()
+  "Return non-nil if the current line continues an expression."
+  (save-excursion
+    (back-to-indentation)
+    (or (js2-looking-at-operator-p)
+        (when (catch 'found
+                (while (and (re-search-backward "\n" nil t)
+                            (let ((state (syntax-ppss)))
+                              (when (nth 4 state)
+                                (goto-char (nth 8 state))) ;; skip comments
+                              (skip-chars-backward " \t")
+                              (if (bolp)
+                                  t
+                                (throw 'found t))))))
+          (backward-char)
+          (when (js2-looking-at-operator-p)
+            (backward-char)
+            (not (looking-at "\\*\\|\\+\\+\\|--\\|/[/*]")))))))
+
+(defun js2-end-of-do-while-loop-p ()
+  "Return non-nil if word after point is `while' of a do-while
+statement, else returns nil. A braceless do-while statement
+spanning several lines requires that the start of the loop is
+indented to the same column as the current line."
+  (interactive)
+  (save-excursion
+    (when (looking-at "\\s-*\\<while\\>")
+      (if (save-excursion
+            (skip-chars-backward "[ \t\n]*}")
+            (looking-at "[ \t\n]*}"))
+          (save-excursion
+            (backward-list) (backward-word 1) (looking-at "\\<do\\>"))
+        (js2-re-search-backward "\\<do\\>" (point-at-bol) t)
+        (or (looking-at "\\<do\\>")
+            (let ((saved-indent (current-indentation)))
+              (while (and (js2-re-search-backward "^[ \t]*\\<" nil t)
+                          (/= (current-indentation) saved-indent)))
+              (and (looking-at "[ \t]*\\<do\\>")
+                   (not (js2-re-search-forward
+                         "\\<while\\>" (point-at-eol) t))
+                   (= (current-indentation) saved-indent))))))))
+
+(defun js2-multiline-decl-indentation ()
+  "Return the declaration indentation column if the current line belongs
+to a multiline declaration statement.  See `js2-pretty-multiline-declarations'."
+  (let (forward-sexp-function ; use Lisp version
+        at-opening-bracket)
+    (save-excursion
+      (back-to-indentation)
+      (when (not (looking-at js2-declaration-keyword-re))
+        (when (looking-at js2-indent-operator-re)
+          (goto-char (match-end 0))) ; continued expressions are ok
+        (while (and (not at-opening-bracket)
+                    (not (bobp))
+                    (let ((pos (point)))
+                      (save-excursion
+                        (js2-backward-sws)
+                        (or (eq (char-before) ?,)
+                            (and (not (eq (char-before) ?\;))
+                                 (prog2 (skip-syntax-backward ".")
+                                     (looking-at js2-indent-operator-re)
+                                   (js2-backward-sws))
+                                 (not (eq (char-before) ?\;)))
+                            (js2-same-line pos)))))
+          (condition-case _
+              (backward-sexp)
+            (scan-error (setq at-opening-bracket t))))
+        (when (looking-at js2-declaration-keyword-re)
+          (goto-char (match-end 0))
+          (1+ (current-column)))))))
+
+(defun js2-ctrl-statement-indentation ()
+  "Return the proper indentation of current line if it is a control statement.
+Returns an indentation if this line starts the body of a control
+statement without braces, else returns nil."
+  (let (forward-sexp-function)
+    (save-excursion
+      (back-to-indentation)
+      (when (and (not (js2-same-line (point-min)))
+                 (not (looking-at "{"))
+                 (js2-re-search-backward "[[:graph:]]" nil t)
+                 (not (looking-at "[{([]"))
+                 (progn
+                   (forward-char)
+                   (when (= (char-before) ?\))
+                     ;; scan-sexps sometimes throws an error
+                     (ignore-errors (backward-sexp))
+                     (skip-chars-backward " \t" (point-at-bol)))
+                   (let ((pt (point)))
+                     (back-to-indentation)
+                     (when (looking-at "}[ \t]*")
+                       (goto-char (match-end 0)))
+                     (and (looking-at js2-possibly-braceless-keywords-re)
+                          (= (match-end 0) pt)
+                          (not (js2-end-of-do-while-loop-p))))))
+        (+ (current-indentation) js2-basic-offset)))))
+
+(defun js2-indent-in-array-comp (parse-status)
+  "Return non-nil if we think we're in an array comprehension.
+In particular, return the buffer position of the first `for' kwd."
+  (let ((bracket (nth 1 parse-status))
+        (end (point)))
+    (when bracket
+      (save-excursion
+        (goto-char bracket)
+        (when (looking-at "\\[")
+          (forward-char 1)
+          (js2-forward-sws)
+          (if (looking-at "[[{]")
+              (let (forward-sexp-function) ; use Lisp version
+                (forward-sexp)             ; skip destructuring form
+                (js2-forward-sws)
+                (if (and (/= (char-after) ?,) ; regular array
+                         (looking-at "for"))
+                    (match-beginning 0)))
+            ;; to skip arbitrary expressions we need the parser,
+            ;; so we'll just guess at it.
+            (if (and (> end (point)) ; not empty literal
+                     (re-search-forward "[^,]]* \\(for\\) " end t)
+                     ;; not inside comment or string literal
+                     (let ((state (parse-partial-sexp bracket (point))))
+                       (not (or (nth 3 state) (nth 4 state)))))
+                (match-beginning 1))))))))
+
+(defun js2-array-comp-indentation (parse-status for-kwd)
+  (if (js2-same-line for-kwd)
+      ;; first continuation line
+      (save-excursion
+        (goto-char (nth 1 parse-status))
+        (forward-char 1)
+        (skip-chars-forward " \t")
+        (current-column))
+    (save-excursion
+      (goto-char for-kwd)
+      (current-column))))
+
+(defun js2-proper-indentation (parse-status)
+  "Return the proper indentation for the current line."
+  (save-excursion
+    (back-to-indentation)
+    (let* ((ctrl-stmt-indent (js2-ctrl-statement-indentation))
+           (at-closing-bracket (looking-at "[]})]"))
+           (same-indent-p (or at-closing-bracket
+                              (looking-at "\\<case\\>[^:]")
+                              (and (looking-at "\\<default:")
+                                   (save-excursion
+                                     (js2-backward-sws)
+                                     (not (memq (char-before) '(?, ?{)))))))
+           (continued-expr-p (js2-continued-expression-p))
+           (declaration-indent (and js2-pretty-multiline-declarations
+                                    (js2-multiline-decl-indentation)))
+           (bracket (nth 1 parse-status))
+           beg indent)
+      (cond
+       ;; indent array comprehension continuation lines specially
+       ((and bracket
+             (>= js2-language-version 170)
+             (not (js2-same-line bracket))
+             (setq beg (js2-indent-in-array-comp parse-status))
+             (>= (point) (save-excursion
+                           (goto-char beg)
+                           (point-at-bol)))) ; at or after first loop?
+        (js2-array-comp-indentation parse-status beg))
+
+       (ctrl-stmt-indent)
+
+       ((and declaration-indent continued-expr-p)
+        (+ declaration-indent js2-basic-offset))
+
+       (declaration-indent)
+
+       (bracket
+        (goto-char bracket)
+        (cond
+         ((looking-at "[({[][ \t]*\\(/[/*]\\|$\\)")
+          (when (save-excursion (skip-chars-backward " \t)")
+                                (looking-at ")"))
+            (backward-list))
+          (back-to-indentation)
+          (and (eq js2-pretty-multiline-declarations 'all)
+               (looking-at js2-declaration-keyword-re)
+               (goto-char (1+ (match-end 0))))
+          (setq indent
+                (cond (same-indent-p
+                       (current-column))
+                      (continued-expr-p
+                       (+ (current-column) (* 2 js2-basic-offset)))
+                      (t
+                       (+ (current-column) js2-basic-offset))))
+          (if (and js2-indent-switch-body
+                   (not at-closing-bracket)
+                   (looking-at "\\_<switch\\_>"))
+              (+ indent js2-basic-offset)
+            indent))
+         (t
+          (unless same-indent-p
+            (forward-char)
+            (skip-chars-forward " \t"))
+          (current-column))))
+
+       (continued-expr-p js2-basic-offset)
+
+       (t 0)))))
+
+(defun js2-lineup-comment (parse-status)
+  "Indent a multi-line block comment continuation line."
+  (let* ((beg (nth 8 parse-status))
+         (first-line (js2-same-line beg))
+         (offset (save-excursion
+                   (goto-char beg)
+                   (if (looking-at "/\\*")
+                       (+ 1 (current-column))
+                     0))))
+    (unless first-line
+      (indent-line-to offset))))
+
+(defun js2-backward-sws ()
+  "Move backward through whitespace and comments."
+  (interactive)
+  (while (forward-comment -1)))
+
+(defun js2-forward-sws ()
+  "Move forward through whitespace and comments."
+  (interactive)
+  (while (forward-comment 1)))
+
+(defun js2-current-indent (&optional pos)
+  "Return column of indentation on current line.
+If POS is non-nil, go to that point and return indentation for that line."
+  (save-excursion
+    (if pos
+        (goto-char pos))
+    (back-to-indentation)
+    (current-column)))
+
+(defun js2-arglist-close ()
+  "Return non-nil if we're on a line beginning with a close-paren/brace."
+  (save-excursion
+    (goto-char (point-at-bol))
+    (js2-forward-sws)
+    (looking-at "[])}]")))
+
+(defun js2-indent-looks-like-label-p ()
+  (goto-char (point-at-bol))
+  (js2-forward-sws)
+  (looking-at (concat js2-mode-identifier-re ":")))
+
+(defun js2-indent-in-objlit-p (parse-status)
+  "Return non-nil if this looks like an object-literal entry."
+  (let ((start (nth 1 parse-status)))
+    (and
+     start
+     (save-excursion
+       (and (zerop (forward-line -1))
+            (not (< (point) start))     ; crossed a {} boundary
+            (js2-indent-looks-like-label-p)))
+     (save-excursion
+       (js2-indent-looks-like-label-p)))))
+
+;; If prev line looks like foobar({ then we're passing an object
+;; literal to a function call, and people pretty much always want to
+;; de-dent back to the previous line, so move the 'basic-offset'
+;; position to the front.
+(defun js2-indent-objlit-arg-p (parse-status)
+  (save-excursion
+    (back-to-indentation)
+    (js2-backward-sws)
+    (and (eq (1- (point)) (nth 1 parse-status))
+         (eq (char-before) ?{)
+         (progn
+           (forward-char -1)
+           (skip-chars-backward " \t")
+           (eq (char-before) ?\()))))
+
+(defun js2-indent-case-block-p ()
+  (save-excursion
+    (back-to-indentation)
+    (js2-backward-sws)
+    (goto-char (point-at-bol))
+    (skip-chars-forward " \t")
+    (looking-at "case\\s-.+:")))
+
+(defun js2-bounce-indent (normal-col parse-status &optional backwards)
+  "Cycle among alternate computed indentation positions.
+PARSE-STATUS is the result of `parse-partial-sexp' from the beginning
+of the buffer to the current point.  NORMAL-COL is the indentation
+column computed by the heuristic guesser based on current paren,
+bracket, brace and statement nesting.  If BACKWARDS, cycle positions
+in reverse."
+  (let ((cur-indent (js2-current-indent))
+        (old-buffer-undo-list buffer-undo-list)
+        ;; Emacs 21 only has `count-lines', not `line-number-at-pos'
+        (current-line (save-excursion
+                        (forward-line 0)  ; move to bol
+                        (1+ (count-lines (point-min) (point)))))
+        positions pos main-pos anchor arglist-cont same-indent
+        basic-offset computed-pos)
+    ;; temporarily don't record undo info, if user requested this
+    (when js2-mode-indent-inhibit-undo
+      (setq buffer-undo-list t))
+    (unwind-protect
+        (progn
+          ;; First likely point:  indent from beginning of previous code line
+          (push (setq basic-offset
+                      (+ (save-excursion
+                           (back-to-indentation)
+                           (js2-backward-sws)
+                           (back-to-indentation)
+                           (current-column))
+                         js2-basic-offset))
+                positions)
+
+          ;; (First + epsilon) likely point:  indent 2x from beginning of
+          ;; previous code line.  Google does it this way.
+          (push (setq basic-offset
+                      (+ (save-excursion
+                           (back-to-indentation)
+                           (js2-backward-sws)
+                           (back-to-indentation)
+                           (current-column))
+                         (* 2 js2-basic-offset)))
+                positions)
+
+          ;; Second likely point:  indent from assign-expr RHS.  This
+          ;; is just a crude guess based on finding " = " on the previous
+          ;; line containing actual code.
+          (setq pos (save-excursion
+                      (forward-line -1)
+                      (goto-char (point-at-bol))
+                      (when (re-search-forward "\\s-+\\(=\\)\\s-+"
+                                               (point-at-eol) t)
+                        (goto-char (match-end 1))
+                        (skip-chars-forward " \t\r\n")
+                        (current-column))))
+          (when pos
+            (cl-incf pos js2-basic-offset)
+            (push pos positions))
+
+          ;; Third likely point:  same indent as previous line of code.
+          ;; Make it the first likely point if we're not on an
+          ;; arglist-close line and previous line ends in a comma, or
+          ;; both this line and prev line look like object-literal
+          ;; elements.
+          (setq pos (save-excursion
+                      (goto-char (point-at-bol))
+                      (js2-backward-sws)
+                      (back-to-indentation)
+                      (prog1
+                          (current-column)
+                        ;; while we're here, look for trailing comma
+                        (if (save-excursion
+                              (goto-char (point-at-eol))
+                              (js2-backward-sws)
+                              (eq (char-before) ?,))
+                            (setq arglist-cont (1- (point)))))))
+          (when pos
+            (if (and (or arglist-cont
+                         (js2-indent-in-objlit-p parse-status))
+                     (not (js2-arglist-close)))
+                (setq same-indent pos))
+            (push pos positions))
+
+          ;; Fourth likely point:  first preceding code with less indentation.
+          ;; than the immediately preceding code line.
+          (setq pos (save-excursion
+                      (back-to-indentation)
+                      (js2-backward-sws)
+                      (back-to-indentation)
+                      (setq anchor (current-column))
+                      (while (and (zerop (forward-line -1))
+                                  (>= (progn
+                                        (back-to-indentation)
+                                        (current-column))
+                                      anchor)))
+                      (setq pos (current-column))))
+          (push pos positions)
+
+          ;; nesting-heuristic position, main by default
+          (push (setq main-pos normal-col) positions)
+
+          ;; delete duplicates and sort positions list
+          (setq positions (sort (delete-dups positions) '<))
+
+          ;; comma-list continuation lines:  prev line indent takes precedence
+          (if same-indent
+              (setq main-pos same-indent))
+
+          ;; common special cases where we want to indent in from previous line
+          (if (or (js2-indent-case-block-p)
+                  (js2-indent-objlit-arg-p parse-status))
+              (setq main-pos basic-offset))
+
+          ;; if bouncing backwards, reverse positions list
+          (if backwards
+              (setq positions (reverse positions)))
+
+          ;; record whether we're already sitting on one of the alternatives
+          (setq pos (member cur-indent positions))
+
+          (cond
+           ;; case 0:  we're one one of the alternatives and this is the
+           ;; first time they've pressed TAB on this line (best-guess).
+           ((and js2-mode-indent-ignore-first-tab
+                 pos
+                 ;; first time pressing TAB on this line?
+                 (not (eq js2-mode-last-indented-line current-line)))
+            ;; do nothing
+            (setq computed-pos nil))
+           ;; case 1:  only one computed position => use it
+           ((null (cdr positions))
+            (setq computed-pos 0))
+           ;; case 2:  not on any of the computed spots => use main spot
+           ((not pos)
+            (setq computed-pos (js2-position main-pos positions)))
+           ;; case 3:  on last position:  cycle to first position
+           ((null (cdr pos))
+            (setq computed-pos 0))
+           ;; case 4:  on intermediate position:  cycle to next position
+           (t
+            (setq computed-pos (js2-position (cl-second pos) positions))))
+
+          ;; see if any hooks want to indent; otherwise we do it
+          (cl-loop with result = nil
+                   for hook in js2-indent-hook
+                   while (null result)
+                   do
+                   (setq result (funcall hook positions computed-pos))
+                   finally do
+                   (unless (or result (null computed-pos))
+                     (indent-line-to (nth computed-pos positions)))))
+
+      ;; finally
+      (if js2-mode-indent-inhibit-undo
+          (setq buffer-undo-list old-buffer-undo-list))
+      ;; see commentary for `js2-mode-last-indented-line'
+      (setq js2-mode-last-indented-line current-line))))
+
+(defun js2-indent-bounce-backwards ()
+  "Calls `js2-indent-line'.  When `js2-bounce-indent-p',
+cycles between the computed indentation positions in reverse order."
+  (interactive)
+  (js2-indent-line t))
+
+(defun js2-1-line-comment-continuation-p ()
+  "Return t if we're in a 1-line comment continuation.
+If so, we don't ever want to use bounce-indent."
+  (save-excursion
+    (and (progn
+           (forward-line 0)
+           (looking-at "\\s-*//"))
+         (progn
+           (forward-line -1)
+           (forward-line 0)
+           (when (looking-at "\\s-*$")
+             (js2-backward-sws)
+             (forward-line 0))
+           (looking-at "\\s-*//")))))
+
+(defun js2-indent-line (&optional bounce-backwards)
+  "Indent the current line as JavaScript source text."
+  (interactive)
+  (let (parse-status offset indent-col
+        ;; Don't whine about errors/warnings when we're indenting.
+        ;; This has to be set before calling parse-partial-sexp below.
+        (inhibit-point-motion-hooks t))
+    (setq parse-status (save-excursion
+                         (syntax-ppss (point-at-bol)))
+          offset (- (point) (save-excursion
+                              (back-to-indentation)
+                              (point))))
+    (js2-with-underscore-as-word-syntax
+     (if (nth 4 parse-status)
+         (js2-lineup-comment parse-status)
+       (setq indent-col (js2-proper-indentation parse-status))
+       ;; See comments below about `js2-mode-last-indented-line'.
+       (cond
+        ;; bounce-indenting is disabled during electric-key indent.
+        ;; It doesn't work well on first line of buffer.
+        ((and js2-bounce-indent-p
+              (not (js2-same-line (point-min)))
+              (not (js2-1-line-comment-continuation-p)))
+         (js2-bounce-indent indent-col parse-status bounce-backwards))
+        ;; just indent to the guesser's likely spot
+        (t (indent-line-to indent-col))))
+     (when (cl-plusp offset)
+       (forward-char offset)))))
+
+(defun js2-indent-region (start end)
+  "Indent the region, but don't use bounce indenting."
+  (let ((js2-bounce-indent-p nil)
+        (indent-region-function nil)
+        (after-change-functions (remq 'js2-mode-edit
+                                      after-change-functions)))
+    (indent-region start end nil) ; nil for byte-compiler
+    (js2-mode-edit start end (- end start))))
+
+(defvar js2-minor-mode-map
+  (let ((map (make-sparse-keymap)))
+    (define-key map (kbd "C-c C-`") #'js2-next-error)
+    (define-key map [mouse-1] #'js2-mode-show-node)
+    map)
+  "Keymap used when `js2-minor-mode' is active.")
+
+;;;###autoload
+(define-minor-mode js2-minor-mode
+  "Minor mode for running js2 as a background linter.
+This allows you to use a different major mode for JavaScript editing,
+such as `js-mode', while retaining the asynchronous error/warning
+highlighting features of `js2-mode'."
+  :group 'js2-mode
+  :lighter " js-lint"
+  (if js2-minor-mode
+      (js2-minor-mode-enter)
+    (js2-minor-mode-exit)))
+
+(defun js2-minor-mode-enter ()
+  "Initialization for `js2-minor-mode'."
+  (set (make-local-variable 'max-lisp-eval-depth)
+       (max max-lisp-eval-depth 3000))
+  (setq next-error-function #'js2-next-error)
+  (js2-set-default-externs)
+  ;; Experiment:  make reparse-delay longer for longer files.
+  (if (cl-plusp js2-dynamic-idle-timer-adjust)
+      (setq js2-idle-timer-delay
+            (* js2-idle-timer-delay
+               (/ (point-max) js2-dynamic-idle-timer-adjust))))
+  (setq js2-mode-buffer-dirty-p t
+        js2-mode-parsing nil)
+  (set (make-local-variable 'js2-highlight-level) 0) ; no syntax highlighting
+  (add-hook 'after-change-functions #'js2-minor-mode-edit nil t)
+  (add-hook 'change-major-mode-hook #'js2-minor-mode-exit nil t)
+  (when js2-include-jslint-globals
+    (add-hook 'js2-post-parse-callbacks 'js2-apply-jslint-globals nil t))
+  (run-hooks 'js2-init-hook)
+  (js2-reparse))
+
+(defun js2-minor-mode-exit ()
+  "Turn off `js2-minor-mode'."
+  (setq next-error-function nil)
+  (remove-hook 'after-change-functions #'js2-mode-edit t)
+  (remove-hook 'change-major-mode-hook #'js2-minor-mode-exit t)
+  (when js2-mode-node-overlay
+    (delete-overlay js2-mode-node-overlay)
+    (setq js2-mode-node-overlay nil))
+  (js2-remove-overlays)
+  (remove-hook 'js2-post-parse-callbacks 'js2-apply-jslint-globals t)
+  (setq js2-mode-ast nil))
+
+(defvar js2-source-buffer nil "Linked source buffer for diagnostics view")
+(make-variable-buffer-local 'js2-source-buffer)
+
+(cl-defun js2-display-error-list ()
+  "Display a navigable buffer listing parse errors/warnings."
+  (interactive)
+  (unless (js2-have-errors-p)
+    (message "No errors")
+    (cl-return-from js2-display-error-list))
+  (cl-labels ((annotate-list
+               (lst type)
+               "Add diagnostic TYPE and line number to errs list"
+               (mapcar (lambda (err)
+                         (list err type (line-number-at-pos (nth 1 err))))
+                       lst)))
+    (let* ((srcbuf (current-buffer))
+           (errbuf (get-buffer-create "*js-lint*"))
+           (errors (annotate-list
+                    (when js2-mode-ast (js2-ast-root-errors js2-mode-ast))
+                    'js2-error))  ; must be a valid face name
+           (warnings (annotate-list
+                      (when js2-mode-ast (js2-ast-root-warnings js2-mode-ast))
+                      'js2-warning))  ; must be a valid face name
+           (all-errs (sort (append errors warnings)
+                           (lambda (e1 e2) (< (cl-cadar e1) (cl-cadar e2))))))
+      (with-current-buffer errbuf
+        (let ((inhibit-read-only t))
+          (erase-buffer)
+          (dolist (err all-errs)
+            (cl-destructuring-bind ((msg-key beg _end &rest) type line) err
+              (insert-text-button
+               (format "line %d: %s" line (js2-get-msg msg-key))
+               'face type
+               'follow-link "\C-m"
+               'action 'js2-error-buffer-jump
+               'js2-msg (js2-get-msg msg-key)
+               'js2-pos beg)
+              (insert "\n"))))
+        (js2-error-buffer-mode)
+        (setq js2-source-buffer srcbuf)
+        (pop-to-buffer errbuf)
+        (goto-char (point-min))
+        (unless (eobp)
+          (js2-error-buffer-view))))))
+
+(defvar js2-error-buffer-mode-map
+  (let ((map (make-sparse-keymap)))
+    (define-key map "n" #'js2-error-buffer-next)
+    (define-key map "p" #'js2-error-buffer-prev)
+    (define-key map (kbd "RET") #'js2-error-buffer-jump)
+    (define-key map "o" #'js2-error-buffer-view)
+    (define-key map "q" #'js2-error-buffer-quit)
+    map)
+  "Keymap used for js2 diagnostics buffers.")
+
+(defun js2-error-buffer-mode ()
+  "Major mode for js2 diagnostics buffers.
+Selecting an error will jump it to the corresponding source-buffer error.
+\\{js2-error-buffer-mode-map}"
+  (interactive)
+  (setq major-mode 'js2-error-buffer-mode
+        mode-name "JS Lint Diagnostics")
+  (use-local-map js2-error-buffer-mode-map)
+  (setq truncate-lines t)
+  (set-buffer-modified-p nil)
+  (setq buffer-read-only t)
+  (run-hooks 'js2-error-buffer-mode-hook))
+
+(defun js2-error-buffer-next ()
+  "Move to next error and view it."
+  (interactive)
+  (when (zerop (forward-line 1))
+    (js2-error-buffer-view)))
+
+(defun js2-error-buffer-prev ()
+  "Move to previous error and view it."
+  (interactive)
+  (when (zerop (forward-line -1))
+    (js2-error-buffer-view)))
+
+(defun js2-error-buffer-quit ()
+  "Kill the current buffer."
+  (interactive)
+  (kill-buffer))
+
+(defun js2-error-buffer-jump (&rest ignored)
+  "Jump cursor to current error in source buffer."
+  (interactive)
+  (when (js2-error-buffer-view)
+    (pop-to-buffer js2-source-buffer)))
+
+(defun js2-error-buffer-view ()
+  "Scroll source buffer to show error at current line."
+  (interactive)
+  (cond
+   ((not (eq major-mode 'js2-error-buffer-mode))
+    (message "Not in a js2 errors buffer"))
+   ((not (buffer-live-p js2-source-buffer))
+    (message "Source buffer has been killed"))
+   ((not (wholenump (get-text-property (point) 'js2-pos)))
+    (message "There does not seem to be an error here"))
+   (t
+    (let ((pos (get-text-property (point) 'js2-pos))
+          (msg (get-text-property (point) 'js2-msg)))
+      (save-selected-window
+        (pop-to-buffer js2-source-buffer)
+        (goto-char pos)
+        (message msg))))))
+
+;;;###autoload
+(define-derived-mode js2-mode prog-mode "Javascript-IDE"
+  ;; FIXME: Should derive from js-mode.
+  "Major mode for editing JavaScript code."
+  ;; Used by comment-region; don't change it.
+  (set (make-local-variable 'comment-start) "//")
+  (set (make-local-variable 'comment-end) "")
+  (set (make-local-variable 'comment-start-skip) js2-comment-start-skip)
+  (set (make-local-variable 'max-lisp-eval-depth)
+       (max max-lisp-eval-depth 3000))
+  (set (make-local-variable 'indent-line-function) #'js2-indent-line)
+  (set (make-local-variable 'indent-region-function) #'js2-indent-region)
+  (set (make-local-variable 'fill-paragraph-function) #'c-fill-paragraph)
+  (set (make-local-variable 'comment-line-break-function) #'js2-line-break)
+  (set (make-local-variable 'beginning-of-defun-function) #'js2-beginning-of-defun)
+  (set (make-local-variable 'end-of-defun-function) #'js2-end-of-defun)
+  ;; We un-confuse `parse-partial-sexp' by setting syntax-table properties
+  ;; for characters inside regexp literals.
+  (set (make-local-variable 'parse-sexp-lookup-properties) t)
+  ;; this is necessary to make `show-paren-function' work properly
+  (set (make-local-variable 'parse-sexp-ignore-comments) t)
+  ;; needed for M-x rgrep, among other things
+  (put 'js2-mode 'find-tag-default-function #'js2-mode-find-tag)
+
+  (set (make-local-variable 'electric-indent-chars)
+       (append "{}()[]:;,*." electric-indent-chars))
+  (set (make-local-variable 'electric-layout-rules)
+       '((?\; . after) (?\{ . after) (?\} . before)))
+
+  ;; some variables needed by cc-engine for paragraph-fill, etc.
+  (setq c-comment-prefix-regexp js2-comment-prefix-regexp
+        c-comment-start-regexp "/[*/]\\|\\s|"
+        c-line-comment-starter "//"
+        c-paragraph-start js2-paragraph-start
+        c-paragraph-separate "$"
+        c-syntactic-ws-start js2-syntactic-ws-start
+        c-syntactic-ws-end js2-syntactic-ws-end
+        c-syntactic-eol js2-syntactic-eol)
+
+  (let ((c-buffer-is-cc-mode t))
+    ;; Copied from `js-mode'.  Also see Bug#6071.
+    (make-local-variable 'paragraph-start)
+    (make-local-variable 'paragraph-separate)
+    (make-local-variable 'paragraph-ignore-fill-prefix)
+    (make-local-variable 'adaptive-fill-mode)
+    (make-local-variable 'adaptive-fill-regexp)
+    (c-setup-paragraph-variables))
+
+  (setq font-lock-defaults '(nil t))
+
+  ;; Experiment:  make reparse-delay longer for longer files.
+  (when (cl-plusp js2-dynamic-idle-timer-adjust)
+    (setq js2-idle-timer-delay
+          (* js2-idle-timer-delay
+             (/ (point-max) js2-dynamic-idle-timer-adjust))))
+
+  (add-hook 'change-major-mode-hook #'js2-mode-exit nil t)
+  (add-hook 'after-change-functions #'js2-mode-edit nil t)
+  (setq imenu-create-index-function #'js2-mode-create-imenu-index)
+  (setq next-error-function #'js2-next-error)
+  (imenu-add-to-menubar (concat "IM-" mode-name))
+  (add-to-invisibility-spec '(js2-outline . t))
+  (set (make-local-variable 'line-move-ignore-invisible) t)
+  (set (make-local-variable 'forward-sexp-function) #'js2-mode-forward-sexp)
+
+  (setq js2-mode-functions-hidden nil
+        js2-mode-comments-hidden nil
+        js2-mode-buffer-dirty-p t
+        js2-mode-parsing nil)
+
+  (js2-set-default-externs)
+
+  (when js2-include-jslint-globals
+    (add-hook 'js2-post-parse-callbacks 'js2-apply-jslint-globals nil t))
+
+  (run-hooks 'js2-init-hook)
+
+  (js2-reparse))
+
+(defun js2-mode-exit ()
+  "Exit `js2-mode' and clean up."
+  (interactive)
+  (when js2-mode-node-overlay
+    (delete-overlay js2-mode-node-overlay)
+    (setq js2-mode-node-overlay nil))
+  (js2-remove-overlays)
+  (setq js2-mode-ast nil)
+  (remove-hook 'change-major-mode-hook #'js2-mode-exit t)
+  (remove-from-invisibility-spec '(js2-outline . t))
+  (js2-mode-show-all)
+  (with-silent-modifications
+    (js2-clear-face (point-min) (point-max))))
+
+(defun js2-mode-reset-timer ()
+  "Cancel any existing parse timer and schedule a new one."
+  (if js2-mode-parse-timer
+      (cancel-timer js2-mode-parse-timer))
+  (setq js2-mode-parsing nil)
+  (let ((timer (timer-create)))
+    (setq js2-mode-parse-timer timer)
+    (timer-set-function timer 'js2-mode-idle-reparse (list (current-buffer)))
+    (timer-set-idle-time timer js2-idle-timer-delay)
+    ;; http://debbugs.gnu.org/cgi/bugreport.cgi?bug=12326
+    (timer-activate-when-idle timer nil)))
+
+(defun js2-mode-idle-reparse (buffer)
+  "Run `js2-reparse' if BUFFER is the current buffer, or schedule
+it to be reparsed when the buffer is selected."
+  (cond ((eq buffer (current-buffer))
+         (js2-reparse))
+        ((buffer-live-p buffer)
+         ;; reparse when the buffer is selected again
+         (with-current-buffer buffer
+           (add-hook 'window-configuration-change-hook
+                     #'js2-mode-idle-reparse-inner
+                     nil t)))))
+
+(defun js2-mode-idle-reparse-inner ()
+  (remove-hook 'window-configuration-change-hook
+               #'js2-mode-idle-reparse-inner
+               t)
+  (js2-reparse))
+
+(defun js2-mode-edit (_beg _end _len)
+  "Schedule a new parse after buffer is edited.
+Buffer edit spans from BEG to END and is of length LEN."
+  (setq js2-mode-buffer-dirty-p t)
+  (js2-mode-hide-overlay)
+  (js2-mode-reset-timer))
+
+(defun js2-minor-mode-edit (_beg _end _len)
+  "Callback for buffer edits in `js2-mode'.
+Schedules a new parse after buffer is edited.
+Buffer edit spans from BEG to END and is of length LEN."
+  (setq js2-mode-buffer-dirty-p t)
+  (js2-mode-hide-overlay)
+  (js2-mode-reset-timer))
+
+(defun js2-reparse (&optional force)
+  "Re-parse current buffer after user finishes some data entry.
+If we get any user input while parsing, including cursor motion,
+we discard the parse and reschedule it.  If FORCE is nil, then the
+buffer will only rebuild its `js2-mode-ast' if the buffer is dirty."
+  (let (time
+        interrupted-p
+        (js2-compiler-strict-mode js2-mode-show-strict-warnings))
+    (unless js2-mode-parsing
+      (setq js2-mode-parsing t)
+      (unwind-protect
+          (when (or js2-mode-buffer-dirty-p force)
+            (js2-remove-overlays)
+            (setq js2-mode-buffer-dirty-p nil
+                  js2-mode-fontifications nil
+                  js2-mode-deferred-properties nil)
+            (if js2-mode-verbose-parse-p
+                (message "parsing..."))
+            (setq time
+                  (js2-time
+                   (setq interrupted-p
+                         (catch 'interrupted
+                           (js2-parse)
+                           (with-silent-modifications
+                             ;; if parsing is interrupted, comments and regex
+                             ;; literals stay ignored by `parse-partial-sexp'
+                             (remove-text-properties (point-min) (point-max)
+                                                     '(syntax-table))
+                             (js2-mode-apply-deferred-properties)
+                             (js2-mode-remove-suppressed-warnings)
+                             (js2-mode-show-warnings)
+                             (js2-mode-show-errors)
+                             (if (>= js2-highlight-level 1)
+                                 (js2-highlight-jsdoc js2-mode-ast)))
+                           nil))))
+            (if interrupted-p
+                (progn
+                  ;; unfinished parse => try again
+                  (setq js2-mode-buffer-dirty-p t)
+                  (js2-mode-reset-timer))
+              (if js2-mode-verbose-parse-p
+                  (message "Parse time: %s" time))))
+        (setq js2-mode-parsing nil)
+        (unless interrupted-p
+          (setq js2-mode-parse-timer nil))))))
+
+(defun js2-mode-show-node (event)
+  "Debugging aid:  highlight selected AST node on mouse click."
+  (interactive "e")
+  (mouse-set-point event)
+  (setq deactivate-mark t)
+  (when js2-mode-show-overlay
+    (let ((node (js2-node-at-point))
+          beg end)
+      (if (null node)
+          (message "No node found at location %s" (point))
+        (setq beg (js2-node-abs-pos node)
+              end (+ beg (js2-node-len node)))
+        (if js2-mode-node-overlay
+            (move-overlay js2-mode-node-overlay beg end)
+          (setq js2-mode-node-overlay (make-overlay beg end))
+          (overlay-put js2-mode-node-overlay 'font-lock-face 'highlight))
+        (with-silent-modifications
+          (put-text-property beg end 'point-left #'js2-mode-hide-overlay))
+        (message "%s, parent: %s"
+                 (js2-node-short-name node)
+                 (if (js2-node-parent node)
+                     (js2-node-short-name (js2-node-parent node))
+                   "nil"))))))
+
+(defun js2-mode-hide-overlay (&optional _p1 p2)
+  "Remove the debugging overlay when the point moves.
+P1 and P2 are the old and new values of point, respectively."
+  (when js2-mode-node-overlay
+    (let ((beg (overlay-start js2-mode-node-overlay))
+          (end (overlay-end js2-mode-node-overlay)))
+      ;; Sometimes we're called spuriously.
+      (unless (and p2
+                   (>= p2 beg)
+                   (<= p2 end))
+        (with-silent-modifications
+          (remove-text-properties beg end '(point-left nil)))
+        (delete-overlay js2-mode-node-overlay)
+        (setq js2-mode-node-overlay nil)))))
+
+(defun js2-mode-reset ()
+  "Debugging helper:  reset everything."
+  (interactive)
+  (js2-mode-exit)
+  (js2-mode))
+
+(defun js2-mode-show-warn-or-err (e face)
+  "Highlight a warning or error E with FACE.
+E is a list of ((MSG-KEY MSG-ARG) BEG LEN OVERRIDE-FACE).
+The last element is optional.  When present, use instead of FACE."
+  (let* ((key (cl-first e))
+         (beg (cl-second e))
+         (end (+ beg (cl-third e)))
+         ;; Don't inadvertently go out of bounds.
+         (beg (max (point-min) (min beg (point-max))))
+         (end (max (point-min) (min end (point-max))))
+         (ovl (make-overlay beg end)))
+    (overlay-put ovl 'font-lock-face (or (cl-fourth e) face))
+    (overlay-put ovl 'js2-error t)
+    (put-text-property beg end 'help-echo (js2-get-msg key))
+    (put-text-property beg end 'point-entered #'js2-echo-error)))
+
+(defun js2-remove-overlays ()
+  "Remove overlays from buffer that have a `js2-error' property."
+  (let ((beg (point-min))
+        (end (point-max)))
+    (save-excursion
+      (dolist (o (overlays-in beg end))
+        (when (overlay-get o 'js2-error)
+          (delete-overlay o))))))
+
+(defun js2-error-at-point (&optional pos)
+  "Return non-nil if there's an error overlay at POS.
+Defaults to point."
+  (cl-loop with pos = (or pos (point))
+           for o in (overlays-at pos)
+           thereis (overlay-get o 'js2-error)))
+
+(defun js2-mode-apply-deferred-properties ()
+  "Apply fontifications and other text properties recorded during parsing."
+  (when (cl-plusp js2-highlight-level)
+    ;; We defer clearing faces as long as possible to eliminate flashing.
+    (js2-clear-face (point-min) (point-max))
+    ;; Have to reverse the recorded fontifications list so that errors
+    ;; and warnings overwrite the normal fontifications.
+    (dolist (f (nreverse js2-mode-fontifications))
+      (put-text-property (cl-first f) (cl-second f) 'font-lock-face (cl-third f)))
+    (setq js2-mode-fontifications nil))
+  (dolist (p js2-mode-deferred-properties)
+    (apply #'put-text-property p))
+  (setq js2-mode-deferred-properties nil))
+
+(defun js2-mode-show-errors ()
+  "Highlight syntax errors."
+  (when js2-mode-show-parse-errors
+    (dolist (e (js2-ast-root-errors js2-mode-ast))
+      (js2-mode-show-warn-or-err e 'js2-error))))
+
+(defun js2-mode-remove-suppressed-warnings ()
+  "Take suppressed warnings out of the AST warnings list.
+This ensures that the counts and `next-error' are correct."
+  (setf (js2-ast-root-warnings js2-mode-ast)
+        (js2-delete-if
+         (lambda (e)
+           (let ((key (caar e)))
+             (or
+              (and (not js2-strict-trailing-comma-warning)
+                   (string-match "trailing\\.comma" key))
+              (and (not js2-strict-cond-assign-warning)
+                   (string= key "msg.equal.as.assign"))
+              (and js2-missing-semi-one-line-override
+                   (string= key "msg.missing.semi")
+                   (let* ((beg (cl-second e))
+                          (node (js2-node-at-point beg))
+                          (fn (js2-mode-find-parent-fn node))
+                          (body (and fn (js2-function-node-body fn)))
+                          (lc (and body (js2-node-abs-pos body)))
+                          (rc (and lc (+ lc (js2-node-len body)))))
+                     (and fn
+                          (or (null body)
+                              (save-excursion
+                                (goto-char beg)
+                                (and (js2-same-line lc)
+                                     (js2-same-line rc))))))))))
+         (js2-ast-root-warnings js2-mode-ast))))
+
+(defun js2-mode-show-warnings ()
+  "Highlight strict-mode warnings."
+  (when js2-mode-show-strict-warnings
+    (dolist (e (js2-ast-root-warnings js2-mode-ast))
+      (js2-mode-show-warn-or-err e 'js2-warning))))
+
+(defun js2-echo-error (_old-point new-point)
+  "Called by point-motion hooks."
+  (let ((msg (get-text-property new-point 'help-echo)))
+    (when (and (stringp msg)
+               (not (active-minibuffer-window))
+               (not (current-message)))
+      (message msg))))
+
+(defalias 'js2-echo-help #'js2-echo-error)
+
+(defun js2-line-break (&optional _soft)
+  "Break line at point and indent, continuing comment if within one.
+If inside a string, and `js2-concat-multiline-strings' is not
+nil, turn it into concatenation."
+  (interactive)
+  (let ((parse-status (syntax-ppss)))
+    (cond
+     ;; Check if we're inside a string.
+     ((nth 3 parse-status)
+      (if js2-concat-multiline-strings
+          (js2-mode-split-string parse-status)
+        (insert "\n")))
+     ;; Check if inside a block comment.
+     ((nth 4 parse-status)
+      (js2-mode-extend-comment (nth 8 parse-status)))
+     (t
+      (newline-and-indent)))))
+
+(defun js2-mode-split-string (parse-status)
+  "Turn a newline in mid-string into a string concatenation.
+PARSE-STATUS is as documented in `parse-partial-sexp'."
+  (let* ((quote-char (nth 3 parse-status))
+         (at-eol (eq js2-concat-multiline-strings 'eol)))
+    (insert quote-char)
+    (insert (if at-eol " +\n" "\n"))
+    (unless at-eol
+      (insert "+ "))
+    (js2-indent-line)
+    (insert quote-char)
+    (when (eolp)
+      (insert quote-char)
+      (backward-char 1))))
+
+(defun js2-mode-extend-comment (start-pos)
+  "Indent the line and, when inside a comment block, add comment prefix."
+  (let (star single col first-line needs-close)
+    (save-excursion
+      (back-to-indentation)
+      (when (< (point) start-pos)
+        (goto-char start-pos))
+      (cond
+       ((looking-at "\\*[^/]")
+        (setq star t
+              col (current-column)))
+       ((looking-at "/\\*")
+        (setq star t
+              first-line t
+              col (1+ (current-column))))
+       ((looking-at "//")
+        (setq single t
+              col (current-column)))))
+    ;; Heuristic for whether we need to close the comment:
+    ;; if we've got a parse error here, assume it's an unterminated
+    ;; comment.
+    (setq needs-close
+          (or
+           (eq (get-text-property (1- (point)) 'point-entered)
+               'js2-echo-error)
+           ;; The heuristic above doesn't work well when we're
+           ;; creating a comment and there's another one downstream,
+           ;; as our parser thinks this one ends at the end of the
+           ;; next one.  (You can have a /* inside a js block comment.)
+           ;; So just close it if the next non-ws char isn't a *.
+           (and first-line
+                (eolp)
+                (save-excursion
+                  (skip-chars-forward " \t\r\n")
+                  (not (eq (char-after) ?*))))))
+    (delete-horizontal-space)
+    (insert "\n")
+    (cond
+     (star
+      (indent-to col)
+      (insert "* ")
+      (if (and first-line needs-close)
+          (save-excursion
+            (insert "\n")
+            (indent-to col)
+            (insert "*/"))))
+     ((and single
+           (save-excursion
+              (and (zerop (forward-line 1))
+                   (looking-at "\\s-*//"))))
+      (indent-to col)
+      (insert "// ")))
+    ;; Don't need to extend the comment after all.
+    (js2-indent-line)))
+
+(defun js2-beginning-of-line ()
+  "Toggle point between bol and first non-whitespace char in line.
+Also moves past comment delimiters when inside comments."
+  (interactive)
+  (let (node)
+    (cond
+     ((bolp)
+      (back-to-indentation))
+     ((looking-at "//")
+      (skip-chars-forward "/ \t"))
+     ((and (eq (char-after) ?*)
+           (setq node (js2-comment-at-point))
+           (memq (js2-comment-node-format node) '(jsdoc block))
+           (save-excursion
+             (skip-chars-backward " \t")
+             (bolp)))
+      (skip-chars-forward "\* \t"))
+     (t
+      (goto-char (point-at-bol))))))
+
+(defun js2-end-of-line ()
+  "Toggle point between eol and last non-whitespace char in line."
+  (interactive)
+  (if (eolp)
+      (skip-chars-backward " \t")
+    (goto-char (point-at-eol))))
+
+(defun js2-mode-wait-for-parse (callback)
+  "Invoke CALLBACK when parsing is finished.
+If parsing is already finished, calls CALLBACK immediately."
+  (if (not js2-mode-buffer-dirty-p)
+      (funcall callback)
+    (push callback js2-mode-pending-parse-callbacks)
+    (add-hook 'js2-parse-finished-hook #'js2-mode-parse-finished)))
+
+(defun js2-mode-parse-finished ()
+  "Invoke callbacks in `js2-mode-pending-parse-callbacks'."
+  ;; We can't let errors propagate up, since it prevents the
+  ;; `js2-parse' method from completing normally and returning
+  ;; the ast, which makes things mysteriously not work right.
+  (unwind-protect
+      (dolist (cb js2-mode-pending-parse-callbacks)
+        (condition-case err
+            (funcall cb)
+          (error (message "%s" err))))
+    (setq js2-mode-pending-parse-callbacks nil)))
+
+(defun js2-mode-flag-region (from to flag)
+  "Hide or show text from FROM to TO, according to FLAG.
+If FLAG is nil then text is shown, while if FLAG is t the text is hidden.
+Returns the created overlay if FLAG is non-nil."
+  (remove-overlays from to 'invisible 'js2-outline)
+  (when flag
+    (let ((o (make-overlay from to)))
+      (overlay-put o 'invisible 'js2-outline)
+      (overlay-put o 'isearch-open-invisible
+                   'js2-isearch-open-invisible)
+      o)))
+
+;; Function to be set as an outline-isearch-open-invisible' property
+;; to the overlay that makes the outline invisible (see
+;; `js2-mode-flag-region').
+(defun js2-isearch-open-invisible (_overlay)
+  ;; We rely on the fact that isearch places point on the matched text.
+  (js2-mode-show-element))
+
+(defun js2-mode-invisible-overlay-bounds (&optional pos)
+  "Return cons cell of bounds of folding overlay at POS.
+Returns nil if not found."
+  (let ((overlays (overlays-at (or pos (point))))
+        o)
+    (while (and overlays
+                (not o))
+      (if (overlay-get (car overlays) 'invisible)
+          (setq o (car overlays))
+        (setq overlays (cdr overlays))))
+    (if o
+        (cons (overlay-start o) (overlay-end o)))))
+
+(defun js2-mode-function-at-point (&optional pos)
+  "Return the innermost function node enclosing current point.
+Returns nil if point is not in a function."
+  (let ((node (js2-node-at-point pos)))
+    (while (and node (not (js2-function-node-p node)))
+      (setq node (js2-node-parent node)))
+    (if (js2-function-node-p node)
+        node)))
+
+(defun js2-mode-toggle-element ()
+  "Hide or show the foldable element at the point."
+  (interactive)
+  (let (comment fn pos)
+    (save-excursion
+      (cond
+       ;; /* ... */ comment?
+       ((js2-block-comment-p (setq comment (js2-comment-at-point)))
+        (if (js2-mode-invisible-overlay-bounds
+             (setq pos (+ 3 (js2-node-abs-pos comment))))
+            (progn
+              (goto-char pos)
+              (js2-mode-show-element))
+          (js2-mode-hide-element)))
+       ;; //-comment?
+       ((save-excursion
+          (back-to-indentation)
+          (looking-at js2-mode-//-comment-re))
+        (js2-mode-toggle-//-comment))
+       ;; function?
+       ((setq fn (js2-mode-function-at-point))
+        (setq pos (and (js2-function-node-body fn)
+                       (js2-node-abs-pos (js2-function-node-body fn))))
+        (goto-char (1+ pos))
+        (if (js2-mode-invisible-overlay-bounds)
+            (js2-mode-show-element)
+          (js2-mode-hide-element)))
+       (t
+        (message "Nothing at point to hide or show"))))))
+
+(defun js2-mode-hide-element ()
+  "Fold/hide contents of a block, showing ellipses.
+Show the hidden text with \\[js2-mode-show-element]."
+  (interactive)
+  (if js2-mode-buffer-dirty-p
+      (js2-mode-wait-for-parse #'js2-mode-hide-element))
+  (let (node body beg end)
+    (cond
+     ((js2-mode-invisible-overlay-bounds)
+      (message "already hidden"))
+     (t
+      (setq node (js2-node-at-point))
+      (cond
+       ((js2-block-comment-p node)
+        (js2-mode-hide-comment node))
+       (t
+        (while (and node (not (js2-function-node-p node)))
+          (setq node (js2-node-parent node)))
+        (if (and node
+                 (setq body (js2-function-node-body node)))
+            (progn
+              (setq beg (js2-node-abs-pos body)
+                    end (+ beg (js2-node-len body)))
+              (js2-mode-flag-region (1+ beg) (1- end) 'hide))
+          (message "No collapsable element found at point"))))))))
+
+(defun js2-mode-show-element ()
+  "Show the hidden element at current point."
+  (interactive)
+  (let ((bounds (js2-mode-invisible-overlay-bounds)))
+    (if bounds
+        (js2-mode-flag-region (car bounds) (cdr bounds) nil)
+      (message "Nothing to un-hide"))))
+
+(defun js2-mode-show-all ()
+  "Show all of the text in the buffer."
+  (interactive)
+  (js2-mode-flag-region (point-min) (point-max) nil))
+
+(defun js2-mode-toggle-hide-functions ()
+  (interactive)
+  (if js2-mode-functions-hidden
+      (js2-mode-show-functions)
+    (js2-mode-hide-functions)))
+
+(defun js2-mode-hide-functions ()
+  "Hides all non-nested function bodies in the buffer.
+Use \\[js2-mode-show-all] to reveal them, or \\[js2-mode-show-element]
+to open an individual entry."
+  (interactive)
+  (if js2-mode-buffer-dirty-p
+      (js2-mode-wait-for-parse #'js2-mode-hide-functions))
+  (if (null js2-mode-ast)
+      (message "Oops - parsing failed")
+    (setq js2-mode-functions-hidden t)
+    (js2-visit-ast js2-mode-ast #'js2-mode-function-hider)))
+
+(defun js2-mode-function-hider (n endp)
+  (when (not endp)
+    (let ((tt (js2-node-type n))
+          body beg end)
+      (cond
+       ((and (= tt js2-FUNCTION)
+             (setq body (js2-function-node-body n)))
+        (setq beg (js2-node-abs-pos body)
+              end (+ beg (js2-node-len body)))
+        (js2-mode-flag-region (1+ beg) (1- end) 'hide)
+        nil)   ; don't process children of function
+       (t
+        t))))) ; keep processing other AST nodes
+
+(defun js2-mode-show-functions ()
+  "Un-hide any folded function bodies in the buffer."
+  (interactive)
+  (setq js2-mode-functions-hidden nil)
+  (save-excursion
+    (goto-char (point-min))
+    (while (/= (goto-char (next-overlay-change (point)))
+               (point-max))
+      (dolist (o (overlays-at (point)))
+        (when (and (overlay-get o 'invisible)
+                   (not (overlay-get o 'comment)))
+          (js2-mode-flag-region (overlay-start o) (overlay-end o) nil))))))
+
+(defun js2-mode-hide-comment (n)
+  (let* ((head (if (eq (js2-comment-node-format n) 'jsdoc)
+                   3  ; /**
+                 2))  ; /*
+         (beg (+ (js2-node-abs-pos n) head))
+         (end (- (+ beg (js2-node-len n)) head 2))
+         (o (js2-mode-flag-region beg end 'hide)))
+    (overlay-put o 'comment t)))
+
+(defun js2-mode-toggle-hide-comments ()
+  "Folds all block comments in the buffer.
+Use \\[js2-mode-show-all] to reveal them, or \\[js2-mode-show-element]
+to open an individual entry."
+  (interactive)
+  (if js2-mode-comments-hidden
+      (js2-mode-show-comments)
+    (js2-mode-hide-comments)))
+
+(defun js2-mode-hide-comments ()
+  (interactive)
+  (if js2-mode-buffer-dirty-p
+      (js2-mode-wait-for-parse #'js2-mode-hide-comments))
+  (if (null js2-mode-ast)
+      (message "Oops - parsing failed")
+    (setq js2-mode-comments-hidden t)
+    (dolist (n (js2-ast-root-comments js2-mode-ast))
+      (when (js2-block-comment-p n)
+        (js2-mode-hide-comment n)))
+    (js2-mode-hide-//-comments)))
+
+(defun js2-mode-extend-//-comment (direction)
+  "Find start or end of a block of similar //-comment lines.
+DIRECTION is -1 to look back, 1 to look forward.
+INDENT is the indentation level to match.
+Returns the end-of-line position of the furthest adjacent
+//-comment line with the same indentation as the current line.
+If there is no such matching line, returns current end of line."
+  (let ((pos (point-at-eol))
+        (indent (current-indentation)))
+    (save-excursion
+      (while (and (zerop (forward-line direction))
+                  (looking-at js2-mode-//-comment-re)
+                  (eq indent (length (match-string 1))))
+        (setq pos (point-at-eol)))
+      pos)))
+
+(defun js2-mode-hide-//-comments ()
+  "Fold adjacent 1-line comments, showing only snippet of first one."
+  (let (beg end)
+    (save-excursion
+      (goto-char (point-min))
+      (while (re-search-forward js2-mode-//-comment-re nil t)
+        (setq beg (point)
+              end (js2-mode-extend-//-comment 1))
+        (unless (eq beg end)
+          (overlay-put (js2-mode-flag-region beg end 'hide)
+                       'comment t))
+        (goto-char end)
+        (forward-char 1)))))
+
+(defun js2-mode-toggle-//-comment ()
+  "Fold or un-fold any multi-line //-comment at point.
+Caller should have determined that this line starts with a //-comment."
+  (let* ((beg (point-at-eol))
+         (end beg))
+    (save-excursion
+      (goto-char end)
+      (if (js2-mode-invisible-overlay-bounds)
+          (js2-mode-show-element)
+        ;; else hide the comment
+        (setq beg (js2-mode-extend-//-comment -1)
+              end (js2-mode-extend-//-comment 1))
+        (unless (eq beg end)
+          (overlay-put (js2-mode-flag-region beg end 'hide)
+                       'comment t))))))
+
+(defun js2-mode-show-comments ()
+  "Un-hide any hidden comments, leaving other hidden elements alone."
+  (interactive)
+  (setq js2-mode-comments-hidden nil)
+  (save-excursion
+    (goto-char (point-min))
+    (while (/= (goto-char (next-overlay-change (point)))
+               (point-max))
+      (dolist (o (overlays-at (point)))
+        (when (overlay-get o 'comment)
+          (js2-mode-flag-region (overlay-start o) (overlay-end o) nil))))))
+
+(defun js2-mode-display-warnings-and-errors ()
+  "Turn on display of warnings and errors."
+  (interactive)
+  (setq js2-mode-show-parse-errors t
+        js2-mode-show-strict-warnings t)
+  (js2-reparse 'force))
+
+(defun js2-mode-hide-warnings-and-errors ()
+  "Turn off display of warnings and errors."
+  (interactive)
+  (setq js2-mode-show-parse-errors nil
+        js2-mode-show-strict-warnings nil)
+  (js2-reparse 'force))
+
+(defun js2-mode-toggle-warnings-and-errors ()
+  "Toggle the display of warnings and errors.
+Some users don't like having warnings/errors reported while they type."
+  (interactive)
+  (setq js2-mode-show-parse-errors (not js2-mode-show-parse-errors)
+        js2-mode-show-strict-warnings (not js2-mode-show-strict-warnings))
+  (if (called-interactively-p 'any)
+      (message "warnings and errors %s"
+               (if js2-mode-show-parse-errors
+                   "enabled"
+                 "disabled")))
+  (js2-reparse 'force))
+
+(defun js2-mode-customize ()
+  (interactive)
+  (customize-group 'js2-mode))
+
+(defun js2-mode-forward-sexp (&optional arg)
+  "Move forward across one statement or balanced expression.
+With ARG, do it that many times.  Negative arg -N means
+move backward across N balanced expressions."
+  (interactive "p")
+  (setq arg (or arg 1))
+  (save-restriction
+    (widen) ;; `blink-matching-open' calls `narrow-to-region'
+    (js2-reparse)
+    (let (forward-sexp-function
+          node (start (point)) pos lp rp child)
+      (cond
+       ;; backward-sexp
+       ;; could probably make this better for some cases:
+       ;;  - if in statement block (e.g. function body), go to parent
+       ;;  - infix exprs like (foo in bar) - maybe go to beginning
+       ;;    of infix expr if in the right-side expression?
+       ((and arg (cl-minusp arg))
+        (dotimes (_ (- arg))
+          (js2-backward-sws)
+          (forward-char -1)   ; Enter the node we backed up to.
+          (when (setq node (js2-node-at-point (point) t))
+            (setq pos (js2-node-abs-pos node))
+            (let ((parens (js2-mode-forward-sexp-parens node pos)))
+              (setq lp (car parens)
+                    rp (cdr parens)))
+            (when (and lp (> start lp))
+              (if (and rp (<= start rp))
+                  ;; Between parens, check if there's a child node we can jump.
+                  (when (setq child (js2-node-closest-child node (point) lp t))
+                    (setq pos (js2-node-abs-pos child)))
+                ;; Before both parens.
+                (setq pos lp)))
+            (let ((state (parse-partial-sexp start pos)))
+              (goto-char (if (not (zerop (car state)))
+                             ;; Stumble at the unbalanced paren if < 0, or
+                             ;; jump a bit further if > 0.
+                             (scan-sexps start -1)
+                           pos))))
+          (unless pos (goto-char (point-min)))))
+       (t
+        ;; forward-sexp
+        (dotimes (_ arg)
+          (js2-forward-sws)
+          (when (setq node (js2-node-at-point (point) t))
+            (setq pos (js2-node-abs-pos node))
+            (let ((parens (js2-mode-forward-sexp-parens node pos)))
+              (setq lp (car parens)
+                    rp (cdr parens)))
+            (or
+             (when (and rp (<= start rp))
+               (if (> start lp)
+                   (when (setq child (js2-node-closest-child node (point) rp))
+                     (setq pos (js2-node-abs-end child)))
+                 (setq pos (1+ rp))))
+             ;; No parens or child nodes, looks for the end of the curren node.
+             (cl-incf pos (js2-node-len
+                           (if (js2-expr-stmt-node-p (js2-node-parent node))
+                               ;; Stop after the semicolon.
+                               (js2-node-parent node)
+                             node))))
+            (let ((state (save-excursion (parse-partial-sexp start pos))))
+              (goto-char (if (not (zerop (car state)))
+                             (scan-sexps start 1)
+                           pos))))
+          (unless pos (goto-char (point-max)))))))))
+
+(defun js2-mode-forward-sexp-parens (node abs-pos)
+  "Return a cons cell with positions of main parens in NODE."
+  (cond
+   ((or (js2-array-node-p node)
+        (js2-object-node-p node)
+        (js2-comp-node-p node)
+        (memq (aref node 0) '(cl-struct-js2-block-node cl-struct-js2-scope)))
+    (cons abs-pos (+ abs-pos (js2-node-len node) -1)))
+   ((js2-paren-expr-node-p node)
+    (let ((lp (js2-node-lp node))
+          (rp (js2-node-rp node)))
+      (cons (when lp (+ abs-pos lp))
+            (when rp (+ abs-pos rp)))))))
+
+(defun js2-node-closest-child (parent point limit &optional before)
+  (let* ((parent-pos (js2-node-abs-pos parent))
+         (rpoint (- point parent-pos))
+         (rlimit (- limit parent-pos))
+         (min (min rpoint rlimit))
+         (max (max rpoint rlimit))
+         found)
+    (catch 'done
+      (js2-visit-ast
+       parent
+       (lambda (node _end-p)
+         (if (eq node parent)
+             t
+           (let ((pos (js2-node-pos node)) ;; Both relative values.
+                 (end (+ (js2-node-pos node) (js2-node-len node))))
+             (when (and (>= pos min) (<= end max)
+                        (if before (< pos rpoint) (> end rpoint)))
+               (setq found node))
+             (when (> end rpoint)
+               (throw 'done nil)))
+           nil))))
+    found))
+
+(defun js2-errors ()
+  "Return a list of errors found."
+  (and js2-mode-ast
+       (js2-ast-root-errors js2-mode-ast)))
+
+(defun js2-warnings ()
+  "Return a list of warnings found."
+  (and js2-mode-ast
+       (js2-ast-root-warnings js2-mode-ast)))
+
+(defun js2-have-errors-p ()
+  "Return non-nil if any parse errors or warnings were found."
+  (or (js2-errors) (js2-warnings)))
+
+(defun js2-errors-and-warnings ()
+  "Return a copy of the concatenated errors and warnings lists.
+They are appended:  first the errors, then the warnings.
+Entries are of the form (MSG BEG END)."
+  (when js2-mode-ast
+    (append (js2-ast-root-errors js2-mode-ast)
+            (copy-sequence (js2-ast-root-warnings js2-mode-ast)))))
+
+(defun js2-next-error (&optional arg reset)
+  "Move to next parse error.
+Typically invoked via \\[next-error].
+ARG is the number of errors, forward or backward, to move.
+RESET means start over from the beginning."
+  (interactive "p")
+  (if (not (or (js2-errors) (js2-warnings)))
+      (message "No errors")
+    (when reset
+      (goto-char (point-min)))
+    (let* ((errs (js2-errors-and-warnings))
+           (continue t)
+           (start (point))
+           (count (or arg 1))
+           (backward (cl-minusp count))
+           (sorter (if backward '> '<))
+           (stopper (if backward '< '>))
+           (count (abs count))
+           all-errs err)
+      ;; Sort by start position.
+      (setq errs (sort errs (lambda (e1 e2)
+                              (funcall sorter (cl-second e1) (cl-second e2))))
+            all-errs errs)
+      ;; Find nth error with pos > start.
+      (while (and errs continue)
+        (when (funcall stopper (cl-cadar errs) start)
+          (setq err (car errs))
+          (if (zerop (cl-decf count))
+              (setq continue nil)))
+        (setq errs (cdr errs)))
+      ;; Clear for `js2-echo-error'.
+      (message nil)
+      (if err
+          (goto-char (cl-second err))
+        ;; Wrap around to first error.
+        (goto-char (cl-second (car all-errs)))
+        ;; If we were already on it, echo msg again.
+        (if (= (point) start)
+            (js2-echo-error (point) (point)))))))
+
+(defun js2-down-mouse-3 ()
+  "Make right-click move the point to the click location.
+This makes right-click context menu operations a bit more intuitive.
+The point will not move if the region is active, however, to avoid
+destroying the region selection."
+  (interactive)
+  (when (and js2-move-point-on-right-click
+             (not mark-active))
+    (let ((e last-input-event))
+      (ignore-errors
+        (goto-char (cl-cadadr e))))))
+
+(defun js2-mode-create-imenu-index ()
+  "Return an alist for `imenu--index-alist'."
+  ;; This is built up in `js2-parse-record-imenu' during parsing.
+  (when js2-mode-ast
+    ;; if we have an ast but no recorder, they're requesting a rescan
+    (unless js2-imenu-recorder
+      (js2-reparse 'force))
+    (prog1
+        (js2-build-imenu-index)
+      (setq js2-imenu-recorder nil
+            js2-imenu-function-map nil))))
+
+(defun js2-mode-find-tag ()
+  "Replacement for `find-tag-default'.
+`find-tag-default' returns a ridiculous answer inside comments."
+  (let (beg end)
+    (js2-with-underscore-as-word-syntax
+      (save-excursion
+        (if (and (not (looking-at "[[:alnum:]_$]"))
+                 (looking-back "[[:alnum:]_$]"))
+            (setq beg (progn (forward-word -1) (point))
+                  end (progn (forward-word 1) (point)))
+          (setq beg (progn (forward-word 1) (point))
+                end (progn (forward-word -1) (point))))
+        (replace-regexp-in-string
+         "[\"']" ""
+         (buffer-substring-no-properties beg end))))))
+
+(defun js2-mode-forward-sibling ()
+  "Move to the end of the sibling following point in parent.
+Returns non-nil if successful, or nil if there was no following sibling."
+  (let* ((node (js2-node-at-point))
+         (parent (js2-mode-find-enclosing-fn node))
+         sib)
+    (when (setq sib (js2-node-find-child-after (point) parent))
+      (goto-char (+ (js2-node-abs-pos sib)
+                    (js2-node-len sib))))))
+
+(defun js2-mode-backward-sibling ()
+  "Move to the beginning of the sibling node preceding point in parent.
+Parent is defined as the enclosing script or function."
+  (let* ((node (js2-node-at-point))
+         (parent (js2-mode-find-enclosing-fn node))
+         sib)
+    (when (setq sib (js2-node-find-child-before (point) parent))
+      (goto-char (js2-node-abs-pos sib)))))
+
+(defun js2-beginning-of-defun (&optional arg)
+  "Go to line on which current function starts, and return t on success.
+If we're not in a function or already at the beginning of one, go
+to beginning of previous script-level element.
+With ARG N, do that N times. If N is negative, move forward."
+  (setq arg (or arg 1))
+  (if (cl-plusp arg)
+      (let ((parent (js2-node-parent-script-or-fn (js2-node-at-point))))
+        (when (cond
+               ((js2-function-node-p parent)
+                (goto-char (js2-node-abs-pos parent)))
+               (t
+                (js2-mode-backward-sibling)))
+          (if (> arg 1)
+              (js2-beginning-of-defun (1- arg))
+            t)))
+    (when (js2-end-of-defun)
+      (js2-beginning-of-defun (if (>= arg -1) 1 (1+ arg))))))
+
+(defun js2-end-of-defun ()
+  "Go to the char after the last position of the current function
+or script-level element."
+  (let* ((node (js2-node-at-point))
+         (parent (or (and (js2-function-node-p node) node)
+                     (js2-node-parent-script-or-fn node)))
+         script)
+    (unless (js2-function-node-p parent)
+      ;; Use current script-level node, or, if none, the next one.
+      (setq script (or parent node)
+            parent (js2-node-find-child-before (point) script))
+      (when (or (null parent)
+                (>= (point) (+ (js2-node-abs-pos parent)
+                               (js2-node-len parent))))
+        (setq parent (js2-node-find-child-after (point) script))))
+    (when parent
+      (goto-char (+ (js2-node-abs-pos parent)
+                    (js2-node-len parent))))))
+
+(defun js2-mark-defun (&optional allow-extend)
+  "Put mark at end of this function, point at beginning.
+The function marked is the one that contains point.
+
+Interactively, if this command is repeated,
+or (in Transient Mark mode) if the mark is active,
+it marks the next defun after the ones already marked."
+  (interactive "p")
+  (let (extended)
+    (when (and allow-extend
+               (or (and (eq last-command this-command) (mark t))
+                   (and transient-mark-mode mark-active)))
+      (let ((sib (save-excursion
+                   (goto-char (mark))
+                   (if (js2-mode-forward-sibling)
+                       (point)))))
+        (if sib
+            (progn
+              (set-mark sib)
+              (setq extended t))
+          ;; no more siblings - try extending to enclosing node
+          (goto-char (mark t)))))
+   (when (not extended)
+     (let ((node (js2-node-at-point (point) t)) ; skip comments
+           ast fn stmt parent beg end)
+       (when (js2-ast-root-p node)
+         (setq ast node
+               node (or (js2-node-find-child-after (point) node)
+                        (js2-node-find-child-before (point) node))))
+       ;; only mark whole buffer if we can't find any children
+       (if (null node)
+           (setq node ast))
+       (if (js2-function-node-p node)
+           (setq parent node)
+         (setq fn (js2-mode-find-enclosing-fn node)
+               stmt (if (or (null fn)
+                            (js2-ast-root-p fn))
+                        (js2-mode-find-first-stmt node))
+               parent (or stmt fn)))
+       (setq beg (js2-node-abs-pos parent)
+             end (+ beg (js2-node-len parent)))
+       (push-mark beg)
+       (goto-char end)
+       (exchange-point-and-mark)))))
+
+(defun js2-narrow-to-defun ()
+  "Narrow to the function enclosing point."
+  (interactive)
+  (let* ((node (js2-node-at-point (point) t))  ; skip comments
+         (fn (if (js2-script-node-p node)
+                 node
+               (js2-mode-find-enclosing-fn node)))
+         (beg (js2-node-abs-pos fn)))
+    (unless (js2-ast-root-p fn)
+      (narrow-to-region beg (+ beg (js2-node-len fn))))))
+
+(provide 'js2-mode)
+
+;;; js2-mode.el ends here
diff --git a/packages/context-coloring/scopifier.png b/packages/context-coloring/scopifier.png
new file mode 100644
index 0000000000000000000000000000000000000000..1ec5d104269d700ac1020ad42eb63aa53aa67be1
GIT binary patch
literal 2609
zcmb_eXIGO66GcE!M1lnnabYbBiwV65NPGyP7^xvadQ*xJ2rVjvPH3TpCP?T+dWm#I
zLJwU6S)@ZKt{@5o4WhEEFZ&<fd(O<g_nbL%zs!drnwuJ)6}T+G#l>|NaaYgcm{*R=
z;uOzu7aSu?b8&HV%#G3d$M*jP006*Xu$h?|7K=S|=FG2OdpZCB0fX5rDi{m_063s)
z2>?1eI)EMk=pIiWE5Oh~6XM#>k^`)xqmXH@0!_0SY*xL6!SO0(KmqLgW_f`{WwT01
z4XNREA%KC?D`(|F78Qjuhd>};2-umnoK)a~_cj14!7WOsgF1%TtaOML63{U+f&qpw
zV9-~?SP85O0jEwD2U8PZW`28pJ}I@{a@zJWL@P5G09Me>96pYyG9?z?F?&Kp-O5EO
zdKkz*R0IF~xoh3Hw9TTfuTV7}CaOE)bxa<mmb(Kma~C^PTT_^cp$=(f4enWIn?o@o
z<at%5u;=1osIa+?z~~I8-G=**_4NXw@(N&Z=ITb$^g<Goxwtr<J30=3kc%a!=-MTv
zrN=J4e*CV|B@}NCQM>1E4Y$+36EZTFu{h(=$e5h!PF$whlG?jw8Y}u6Hd$1CuNRij
zQ)6mV_TD6<wnVVULXsQsw`_ftOnuwau!{Ol0RfIRK`0o)jO=RQ=LFbjLL!!?rWeK&
zmPh91Ml8s~?To&~xvqf;n!bNyEtA^aTkB7)afmBS?kUKvFVMwhIzNkBZj1b@#+zR1
zHIRHi#2rP-GEazvr_W4ejnNH5yEDt%Eh1`X88su!ir5lz5HT4+OhjU1Y$Bs$8zN)N
zA_0$x@jQ&<W0#>YIN_<TmWGzzEhR-2CD;Cq(CIbL?#b5q5j>ObRNVWhqBW7$k~UYD
z(OwrvE!$=mmR1!QJ<WTTPiC*=cC_T~%|1&S%*v!^vW8O9XmNu*v7M#CF`-_RB=^Lp
zPOswZN3v|4Tx}F@!##|lUdD<hNqzn=izXQ440?`RSnL~GSV)jRvD|kc&9ce|p{j+5
z@R4op@`*V<dU2eEDT<3v^ephZ1w!p6w*c=Ig^Pb)7v@*j7dN@hc^eJk;^O-Yp$9_;
zE$rk!Mqh+o>d~in++H7tZd}$dz9DojsYyta-iHzvp>y#J=%Bp^czMvHLf#Fp|HF}#
zhx(QJJ#-fI@G$gNNE2(ZfECV4K9cmU=i=e%2VU?B<@r4P<K=wJIZvxXUW#TGe-izs
zCyz)SicvTw$IJUg*GyUkZ0zM2iT-s^rcA--E1%qnk~$)NM8p>n3qJVX=pjC`muL;>
zO}?6|7zgSTy)`MAnR==Fu^{X5e%f1F@|Na8KylF<soBhF--oz4THfq{-Ts<bD1{{Z
z9n0%YVUYTT*$u)V#?Xa1iRq?5znJhpS`P9n`5}P;*RcIVJ?FT;O2D=^KFAPU*q40E
zl&j0x;l(mz4t*AWk?X?H?Y7={R&$g45BuJyxRo?V*(K80HO?IRFkzWTNia0QN>03k
zgXC_<Q?B36HW}}`I~;ypMD2#UlKfjIi?Yj3_LPpM7o&v+LE=Q;WYY5G4ebY^uHN?c
z$w{iW<<Bcj^qfv7#$QvsDKzEs(_S~?_T4rmsJIi!(Ph=cE^h1rW*gD!#-@wSfa(0|
z-{V0cyMml2Zu=?`D>>VSDyRA3k)L2D_x3n6a>fs(P|weOII$wiM5V^s5`mB}er{{N
zCpHm2D}JX+OiSl(KeT3l<eKIBp@jOqsv4`y-!ub0A)pe~cG|BZ?23GxzWc4NTA$|3
zU}`U*fhgQ&w0>ln4c|d^bMIn#f!!KO&j|GX)O(re#4$BPg-nD*gRse+m&}1KnIj70
zM&JBqpeI3+)>t++ujwZs+Bh}cUs}NniZ~RwuvetzzaEYBiCP)7FC+axD1Ti2^X!Kb
z^*CycVJh?}z(lyBEJs@wCKHmT6M#538zk%!v@0TO+ivY|=RlK8DRj_YPd+I>!sfSO
zPEmd!zs7NMJQyuknJVc}D1Pb-@dcvkfI%yF-nFUa=j+7Si1K9)gQUs}pwqtEA%5(t
zpKFD8N#qbk@eQ`6$hM}-`GDOITTv?0S3Y8Ke}6jnOV6`QQZ#5coV$Iv+6|1|n;enc
zo;}0tFCFrIt##sqOZ~v;>A0|~%1VRu%};5=!&hrCrO!}yX5=s{;_R7k5sc@ByxQKG
z`0wljRKN@<+3BD6Dz*#5niV(hn+IJkzfLeSghEufx82S^Nf1g|Up2B>!acYr9bw$_
zsLDgB{2>3XBKd(X_55iA)Fr5a0;_LJ3IAQxfHXDMrif2=?24Uw$oVWM&CyDCR;5Qu
z-tVpMY3AHy*Cr<ZGNxi3qIxD}PwYLdjJI`wUd+l#m=TD@wj2~~gT1A{xykAO!<imM
zW2~u&q*QHW+pc71Ck(Cj@2C_LeqGwYu0?d3;8VEumSD*bPOeoxvG2fjhj^!Skp`NC
z?<aIrm{yv1&nLM5rZu-O#)+P>MA>9yt0A@zehc>OLK56V%3?aC=H$?uyh#fz8-I@%
zR%5|+VIKB-%Ga%Rwo1Mg;Y0*nzUr-^VzGyWVE2u#y>HDM1i4uhXVE)qWv=S8rp|Sp
zW_3@FNN_^WzdLaiA^o;|%~p*4lnz;t=g!9`uD;fz+PWY*<+4$?z`?~GtlJBcV8iO5
zoTC(Wbzg#RLoGC|F`AX9m=#*fg4SM9FA9*Vf=W>YvoOr8MswIk)D9nxUA??;>!#|d
zd>M{;=ldlB{<8-aojvVd6WL7o5^lo?Bexx9_iEMcCZ0kP(w~0u(ms{-q*LtMN{}!U
zBfc7*&V=vV;+@;UF}mgh?`Pk;KPxRN>dBHERh#6QR~O5o6-JJBHtbCIO-Yv4JGgt}
z8?Ma4=U?-3^ma;4CT^a5`=Lx1UmW)Rs&{1ke9JsTf@=^{=s4NzC8;p$gS6>rR<)5Y
z`V#c6-#jh<;VNsj-oaM-)8S~H%=jyL-at@7h4#Uh;`QLpc}HZ)U4=?|rb&q0WL|--
zcxcZynv<8bd8L)_K+j|*IHXN;qzUJFuB`5K;Oo|_tBTg9)$-jUS1wAGPvmCUDg5nz
z-Vv7wMPQL7=+0Eff8=jHp7`C!>w?f*Q1$`Nw>_rs>k%tlmJrBz#D#3Zg>9;qRcQS1
zOw^8PrB0V~n4!z>oU}jR1JyjTbpEx;no%P6Z%v6CkR}xIxVR~bO0r~u3ujA^V(9Vn
z0`!fEx_35oXsRB)#&k=vKIt|kDyvJf8=N`uL!?FE9tP_N?p+ssr@`GF5L*`=Kwi9H
zlm&4m9nFh034Cf+&slg`TvSJa``7fqFc`(xH#p$_wK?09SL#xh|L~5K%E35I-kV&?
q21O`vpK7#@t&J%Hs-6D^i2FPkeN2bsb6Wd<j~_zcRIgswIr@KP!sg`w

literal 0
HcmV?d00001

diff --git a/packages/context-coloring/screenshot.png b/packages/context-coloring/screenshot.png
new file mode 100644
index 0000000000000000000000000000000000000000..89665b77ff45f1330b754682e6c787d22c1a56de
GIT binary patch
literal 22006
zcma&Nby!>9)-_tE6bfxAR;-0Wid!j`kkTSWQ{17ryM+Y#Q79C5Eo_PgcXx+i!HT;D
zch^hLx#xWE``v%;exAtMGUgm(%{AwG_S)O;tE~9bC$FB|xpU{K#1|3yJ9q8@w;$cd
z_iy*iT70y;ZCrhoRusK${{PvaP$&!rV`gUl2!(#SZ9qSY|NXxGD{5tBB`FNW!^49L
z%ZU8l_x1LmjOgttPz5n4R7B0Wr&c4_4f;vZ4e-zqg9?AOs%bJ2meCacYVb+T4Ej+*
zPyW+KaaoJ5S-r+tJ3}$#KXS_P1)t6HKdU$eHs=aUD4BhMO4-4ELe+wDEHq_4BBPBm
ze#k2OxEe&b*00yYV-UqtWscU8+q3y$a|_~Te)h5--F_<cc*tl+{Qfyo?Ud*s;{vZJ
z(}#+E+o;sj7Z<AOaQG?=l}{e}IoX6-$y(0VvPGzScq+LkBIIR039J0f4pRymNEr(F
zick|4vy61}S6dp%46U*c9KZyQ<bPF^(JC2_>d616Cf8g3<79uDfv<Drbg-X+NGY~n
zRM!peEc?aSHehN28EhbKpJ3fmBOBFcq5e6bI3(6u+`6c1X~64;t5lh(c(|(Yz(hjO
zpHU5@L&J12CkwMiTG2$TQuTPMQDUl1q-z6Q_SbM_Zcon8c%eUFShSMzS<kmMG)Tk{
zn4XOOtP|;(>19`%=Vl?6v{bn@67ms-{8m_MlZZ^|&XzLJXJ+Q`>>F#qmh4QXrt68!
zHUIOYsnadi&bYct=93sRyMSJ5b78W&k-A7utg@lCY{c{*|9r>M$*!P+@7rC-u`;KQ
zu~bQA6{T-dUu^<QyMMIyWI5$!Bo(CmnCg-7PW4TWuOIS?cas&<&~l#~32pE!t*{-=
zHWZipD#XETTi5@^qXC^_tpeBiUXt$W8G<Tst`Ah*sW-Ft){AP)h>p@WH;}eOp~Cup
zaelRub@S~CSJvQXQS=IL^08szm6CYR+>5E#P6~pB1sa8iE*F@veb#XZwq32YRtqd2
z?uhis&abEi3>*VX2inpb!t&C;7v}2wmzE}>+r!$!gPW5^lRYMr4C4KRt-JeCwhHJ{
zACLAzq_Z`bsO-Zwh34Dnf9fDFt8nKb$<x<7OcGG}drv4X%pE80+>!q!AtI~@pV&;m
zSicaaZ2NPrF?z?6UbGNbjmShwkG=K5y*HZRIkZ$81u0C^gwJL>=*AQNmiko);$)#s
zf#~Mp^%ElFX!q(<EzqPRZ{>)KUw%&rueA~Nkk<K9{Ez=^ZR)nOEsl5rk>A!j?VW`L
zeD!trNIeN@!0^J-%rcZH;vYh53k!>PUg}8oG~CnO`4$S^QYhU-iK;swA@*gvdzi|?
zr(zP}82dC2hQBMZa(ckaEQ6<GcR@T0@LD6TFB2j5J5b}x9N(?z4PApVV5(@c*P`N_
z2=&3?Lfi$#rJyiw9!-f7GGC!On%D|~aW_~v@GizC3$kZHlWFhB(a`Mr{SXT&1ufrr
zLk7s}gwEaDDzICV0Mx@6Gf@%VLJW3OX9+2Waf>1QI?m2Zq2pFhC6AnV72=L4)Qj@h
zsNNxu3CwnoIoDIBFj|&TgzgsZNS)5<?g;A^CJ09gsf`*Fd=Fh$vwt|5Cnsi&8R)0l
zVcG#IuS&wLF?+jnrzQef7>0Gx62-Nr;%e!t&gPEX6Mz~26%9d~a>tf40Un~po?Y9B
z$69<ZTMlJ)qhHSDI4z6%h2C=1v?f~bR)}-@2eC7J+#T~?oZe$^W$4WB!YIP7UG6+s
zR^UM4biP|@VjI3{4MoI4!I48&4X)*@nT}z#yPP`h##96BM_n%5`NKF#7KH2q1N4qx
zVIC9or8=vXrH~!OwIX!-{&B7wa)VFP>)4_`uk6o<ECss;N7c$g>>PYpz!H{S=c7G2
zExB``r!rY0BnY9#mO+k$&(-TGuGWI3+KK_~o%b3S3Cq)+<Z>2u=+TK({H<2<l)R+b
zb9MFcJZH7(84qiZcQhl&r(NXYGUo1~jL~zx)(zl^+`jK9(1EY7U17nYWd*_g|34m2
z9R*{4L+o`9lhHgTn0ydyvN!94vm0^*wJ5%Y28=N?C6WaL>r8F@hDw%d)7q0G@3r6+
zha*g#Ya_-JGri!gG>AQWyEWs3Y^`~{8k}DR)q-31yV%?JYL^aLP=|u0YJXZzcOPtK
ztt$<fqw#1Ce*AJ2X&9FB;LjiKXfQ;=2mIKwyb$L@Y{^%jI+nNA&JNne8i9+MWVF%*
zk&9AAD#yuIR`kI|#Gj!OIm>$M><ELeV{#~&)3r62hAe5#D(wU=a{X5jBQHfa?`YwW
zEzVt}d>@~g`V4f=w_swDX9l^xH;+4}@MohhoK%P(HuC&Rp8aaMQl7@O7*e|`5@TAo
z2CMNt#-oZU$3nayZS$tZs8_&~%{9I*;JVH@Rn>ip1rk^F#0z`^H2m5gVa}j9wt7FQ
zV4hi_#x#9hgXnm)CBeT~_;lAd5eOs<Sv!pUD}AM!_@PwYA?e~P7;Kzc+Z6V1npsz$
z#5Iv5F}@5cUXUY*EF)^0KMfK9uF9FLg;b~s-0047fU7ppFU>65NGnMPu{OKf?FN`B
zR7IRR@=UR}d~`ziFF}j@m%FCF{-|tk_1_BomEicO&h(6<@m66&*Z&X2VPVLXDXB+8
zT5w`=fj~8+!S%2_G6xNu`wb;}L>@2M!z>Apiq+(CGJtXWt19e0XD%&__}1BYe^P$Q
zk$d$LYqt1=l@Tg8iL4`iIpP~S>^M!xd1|lyp+-ky+qgtO5Xnt2dFSS#j$x&0F3Vd4
z84Y!p8}d4~+L&H^zq!MeJn6r^{=Y{~na*lIm>ti<0_JFf$ga&1eQC2}I?DR!)27-*
zZE1PEx+*fC*a$iC<!71kt8ZJTVS1{!xsPlFAYt^m&4~Vcq+95nfYIk5<e(E4!`9R|
z+iK}j*Qd(HX*?cc$GwAaNTDNvxgz5iJD0*FRz>b?(M@o>Z<Y<#1VZNLD@JOytyyBW
zP)SWBVlzvQV%=5EC6AE)d|dsuOP|^$LLkCm<g0~p^PpwDmdDE*{p3)e#9BzNtwCb+
ziGCH=BPnU+_8+FOx^m+@$5@aLp7fPp*^7gxes#@Wkf1&7h1%U`70W#5FG7|dL6)v0
zV`I6m=2F4xmjd;DKRGS3nAMOz9brYFomb?duNjyZn@dsJtV))A6AmH?<D5qz^^ZNg
zEXPj>kKeZ;{SKPF6yQPiY5G<BcdWB$dBkG=Z#0R&H0O`ser+SPA}7s&995C_mJ75{
zJ~#ew3>uOA%KC4t2g5&cD7O6mB6;4V9u<$hV$>QQBCOBc+wgYu=_u^24zEB3()mtC
zIKSdli~cMff-r<Rb6WES|Kz5)ATp<4p&&v2=rDjiC;Tu$<mT65_b|IxJYWws!*+D&
z#cp;f@Fp=#e@s5+Il3dP1qNGaFha@~Tw7!xB$2Uk+<Iar0FklP`d*&_w;o%Q*jc(|
zbic48HKM~y#uRp%(q2F`LQdWCVS|hAjE@Vp-733W2jW!YhfFB!Kwg!JbM<Y;p4-Dj
z{-sUG#Od0JbU#2#ZBYt34-Z~m|3NR&eIqTX96~k-?1~2IfYP`>;ZNpv2evMDx^7Es
zDz<;~LwBtW0EnIIl5D+$*0^NO!&`a&4d<>JTa}tyiVDPOX>r<*kF^;r(wz1vtU@$o
z%I=SQpW{LP7bY{Qc)fsd*|~+CsL{e=LkOJ?6>>Z1AXrq=18cQqYWr}@bkC!4Vu-e_
z@VN8~XD)<QK2Y`e?MgS#-apy=43so`N6%RTS}?B7E(#ozWUBg<YAf%m#IDbkpYb!`
zkCf=HMiuB!Mx3?I@oeCSP~F^5auW%rE0_gVVt17aK?k=(kMSmYji)tCv?qp{b0k33
zQkQ&@$Gv9P<)V{w-mIV~e*g6cDT^^0=t2eFFTJ`|o;Ewfg!s`wI&28R3Q_y7%?j#Y
z`W?En>lWuE#`(pRx%|P>_)23D@0wtC+jFI!uOFq_;JrC{8Jb3{5>Y>xI_hg}S$g@n
ziGwuBTX|!k6TgL;dZ*P)w-(KY4~f-nhPPmODJ+zsS5P0PdGDi)$&!g=rRDSCv#l2J
zbhUY22f5&OZXbGq<x$On6=juPJ#8SdGTx$$yYvo>a~{)j=NcK#p(0gstx#jrj2PO>
zbyHeicXYqtfK_a(Z`ig63+C7t^93|**HVb*#}+)+|M9089-%dMp)Y-U+E5QeoSB)9
zc$kqAs9J-8vVv^Qx;|@~m9-Hqb4cHqt+Asli81F`qVr-W@d2@JZlU_0mE+5Jz2=UV
zhS4Ey*4}g2R`cBr%MjzXtmXCjV+Zi0<EVNFx6_nbVnmyov=g?M&#ui?boO1LI+=Ab
z9?~Ni_h!oK#@(^({RXrQPsR1aeEI%_ic-GS)$Z1KUd<RHp=hyhJ%Ob!1^dUnmi&MF
ztRQ{*T&JMq-yY|mS~OcO|JP;H5iN`Vx;${82X*JAJY%zAwg{~<;qz=N{$~4=O?-v;
zo|1g(9Re|<{Uxfjj~a~Y6Tcrf=sppPz`i4Un2NT4iEqde4;H`sj~h2!h9Y?3RA-lh
zq5P+8Q5)mN%PkX?fnxn<BVIVHxr&eUWpIb20wN}szj<}^akX;pzBNhiRFw%!oY?Ab
zwwZ41R(^+%$mJj`gX9_(#&oh5v1Sn?OkV5s?*=r~o-~Hc){^d$NVO;ou_~*8(b$J(
z9sg8kLpMSFGM()`8!x%%oHb+gX0Crk9iY$^54Y#@Oip;FUjrXPb|g2&0JS##*(vd5
z_J*C%0wURH_LO8gV74sfOUbKG<MiyGf{j{&zx5sjjWXO#4TA*i6W+l#IWl5<39H#O
zA*tf=iW&LvN7FlUD&V5Xy`F?|s$Q>@FxDzDjf;#g+z~JK-V()my_}1ab#t?`dGdSO
z(6xH|da8~MWIrR++vES_y7qS#2yBTjo@Y<&)yti}p%cqF4%QiKM)MVhzx#T~tdRB$
z#Wx`yN=HfGy!b8a$eG0ZL;@ZiEbmuhG%&!@oT3|&#_N1^50G_oc1(J<E3(4H$JAvF
zdbkRD(!G4@bGL&IYc`x3uhZqV5x)PZd+(^t)H@JBvd9p6;dxBrnZmQRMjc?}7=7j`
zWk&<24My9W8r&Mtad}OTPuslqn_VGX%=-LJ>X0&Lj2~@e?*rO359@pPfW{47G~0rN
zd$7xVi=XX4#15S4OoB9+ckY<N&bI2L)e-GNio%KYr{2}P&Z4+4D`)lM9@?%FRobWE
zxK0jBf;(`z6W!Bl<!*qmoJK#~iP-a|K8Wx(ejgqjz3~qy?&re?@8=&TYd@H?9AOff
zR?~NFYLK90FFv}<Pi_dN(~!7hH$nhkXr2r2lkgKLr<E~Nz`(m=wc8a8KE1`ih10w^
z;u~871~eBj;iG8f&rQmSs}3+E1%_jeKXB0y)bYlP3mE5<8KNXBcux9gdre>mIEY7P
z%ht`2x5J&BG2L*V*NQo*CxH{ny!tcA41`Q-f(Y2IFgXkj@#HyB+V)e6+<nX5V<DS&
zqL`fThvL^95S-!yDo<J&esC7e&nVo5(P<EaoEsygcR8(<tH)986A?8w>47Mjb%>jY
zy9POIjCcCv&UzF4Q`^K+zPlC?EYf)hs!9$su!3#h5##SW`}r*>_1$)>Kgg0Eyk`$<
zB#P#=u5zoNIh#5dqg`Z_*-kC{6T{OGi8D%{=i6nQiMkK#`1ZmSer>!HsPyVlGvD<C
zkPMsr1QY8Q|MZbI`8LwWwqf<20Se+Yucu4ga$xg{Q^aWm;{ryGT`SL?e2-})4Z-&h
z9mG4k@hLyMw#=ZZs+n1qckcN$OqrXbYP?yi(>?fpkonjpk7{L&k)cCr^-MY1wz|F}
z&1jnAE9}m1Q0Co!A~O8L2aoPUJexMPU?AKg0-{YxOv~HCR%mQ{et(gQvoa(0V8QyZ
zy~G*9{Tnov1uVWZ;m6d!EcyBF*n8D*$G*wNEi(Mm)xx_cN`|H|zy0=Um#=7LI|7I>
zJz|2n;3yRp(dve6RO;BPCHi4`O)`46o_Uxinlps0M-4hKf+*1ZNxZ)_Iv}5V)8muF
z@=>Eo9dzoCIRn}GvDF#I@5+(~bg#Rr`nJx|g<FY@?6G;MRk!7!^H46m5c_;{#G~j8
zY%Jjc<v;63(ynnk{=XiL`~)Yr92EoSe~1v@*N+U)6QC^itv_+;?=6B-`^$`feZXpx
z>U{z7jjMLhF2Xx2_uVk<@?pTj`*gnjbbC<|4PNV>Kfa$ZDQxYya{Y!7<a0*781ZdU
zZ4SN;f!>wk6SdR^xN8%uMQ<A%!Y;IN7z*j<fCxu;0*&8(gE(TXkNd7GvX}@KJ%oD^
zlPek1oz?Gu{lZtA3Xps+v!!Z)Tlv&LjI@cVIxkEn`i<>+tEi6!9ucX?&qp?RV&B#G
z483ssV#q}nDfHf7e*BL6^DZZo9LHY5B)DeMbZ6U)eS2!(&HeYrRuwkEh^GD|;dtC0
z7Z%=13zm4n{CO82->S6(cC`~pQC2KeRy9{JlHLXnyy1tWLk#W#KR><zQ18-$r5j6j
zlhUXxud4)t&d5<84je#W+Nd}YxY?%(u3hikcA~6ag#`&YeNC?UaUC8VslX%3sbs@9
zZxU5+B=>vff^oCjI;B5l7oR5J`0u?GOYj}mr+c%<#&J3|KGtT(nkFRQXTBPzx=DL~
zklW$Ih{leYF1aj7)^)kX<%@HBEeF92&WhLK7fZ2qZmzT!d&BZ^5_WTINl3S7-}F%z
zcQ{6+QGO{@Hn-s#nIH3mb%Ad%ST3;@>mEI~Y-IX{&l-F*_OTQ1DZUu<m6tx#2r$lq
zi<uS5U}8pyYyEJ{R)vHW`nu>+))MyMbc9U=4jb!(U6{DXGwGWmQcL06mTG%RJI=Rk
zIu0^esA&QgJ2zSjbl`pN*KQ=@)YvyiWG!wZ>P?63j`skWyVPGXgVCPY@;-ts)cBTu
zz5%K8QiPs18P3lAgC^Uw4|GT4WH9Xuv=}Qca9PppVY^N2JDO*HHTL7qXDSS+n383J
ztRu`RP^aNELFY4XK?2mBT0Uf8HUY+S*1DdR2f08ndm4LeK2Rv-`lb5|AC@Ml|9{Z$
zcQfXIwt~`{xNWxFEZ7?}v+b*C9s+)Uh&p<+L9=Qsx<~lf`4}N>2=iA+TNz3f@{3Z_
z=nv6BGasBLmN3{ntCtp#dH@dVl74eMIG5$Be01%&$E3=^OAasxkd|zKg6X!%M8)8J
zoQR|o--A#DSQ|3?CvAa$Fq&u-5MQZ*<^~i%x=$xcLjI^}IvkaOR({y-3USa-hInNy
z`jwg-(}-|iw$a=g_+X~C>tJENTxNZ|hVzJGGGgAvMEv38*#e)_`WGdVqyyH6JA}9L
zjvd;=HmCT3v8t1ioUtlyO@h|3J}M28|4kg0CR*~{y|C>2T|aT)FSyI@`^frZX<=33
zg<8dj;N%;gGV-h*gVa7%a8&PRN<0BT?M}+Bb)#>_Egy##ODuw)7q~478NY%*dZa(l
zk!fZa$kqDdQ<wB};H{r1Jp3)myhOO_;Jn|j7uP6mbXT;lBoS=mDq%Z%w1f=@IMhm|
zqNf5hy+P=E;JgY7X5QSKxzN1>CdmND%4{cxhLQ7V|LbpfK5nX}{F-uzF)~<Sh|5B^
zL?A3&!F0~FZkmiI^yXqOH{nmCPm<f8GHWXh^}v5!Fa|v_Dr@*ti_ilai54T=RAtk>
zP)APsR2cCF516q`jpy;_%|{dF12h&*g@h&Fdt~fn=_a|_JHVcxcF;v(f+P-XW2?O}
zVqm|A;3_3&c9!buF8i3TnhGCxmL>V3KEUVU>KYy?c^?+A)xPjZoKe?O1Ux{q``s<}
zB*JT*aV+}jo@R44NNqA*`4wE<l()T&7+!7d%8~PJTMHMS0-eLX;Aoky+M=6_Gtp>2
zjRkGCk|k}n*fBi?vzAh{F!a3EQjsg#4$;J{DQ|(F!wzx4x-`m?1cV=Ci|jl8&-q;s
zV7OcDM52s6<ZMLX<q`@QNjisdd2Z_vqNeD^Ky$Xgc(*IkA^O=1zumXvpS(d5dVhN-
z>I2aAJ+CQV{lEz@CaD@M?r^CHWZ2FiDX^GPi#t4YK{61cN*$boC67(-t`#q?_2wiE
zrq(aaU-D7sSbwgGv_S8gGBNv<1&c&Ysl|cM4*HfOT&CT5+%~NQ(}PeA>zuy)z}jSd
zo%woV)?8c(GQ|1?P;(MWhjzLRf}QBEHpZ(&Jc2vXEGM-&TH`Ja?i8=sjNV`NQ*DCR
z&DN|4d_Ex)B{eeDYAQ*29yz<mZG04btRvrSgs;J6(VF|pN~>++$TEBBCbA~Pxx{_H
zW3TlzxcPo>6MNNN3Db+$NxP*rEZKknK(l0w<)kFFKs2kUVm}?VP7KwoL`<b*Lxf6+
z_Pl$^%wcs1wlT%NNu?m8B0v$7H=C79gZhbbcJ93_W(`mb&CMxXCsB8qkd0nc+gS?X
zuPJ&S9|$V&>}n$1ACPS&`k?<DHUs0)3G2(r($XXj$<oE<&;s<%`3v9nGw4-a+r<fs
z^(2db-DZ6y{3yA`2<{e%&w|fkV1^tZfnSym=Z~BI=Xb-J5&szL*c-olxt=YZ3y3O}
zT2~~oG!J#}?t<@wdvlEC!7*eDSv#$2<J0al9p~hZRoJ^QH&?gRw9-9K$L0QsA)n&M
zFwC*2*?>G1%yLUj8M`2A;Syek`U%=>q6Mmh;*y%O1jpJ6W^132vM$5fW(3xUJB+W)
zvpO$3T}Um1HrdQ~4Qd^n6E9>shy<N|{uHewBHgZ~lD~1hPY6k@`HRy?RBkZco?H5~
z#JKi)U(}$l`75T1n-X@iw=zdf(?067ttCFbmlD-Y<&By)Ok(G`A0v;u?Z6H$crvU0
z=PQBNmH`cz%6?n+E^Jw$AD1}N$Hd-%cl<GIFbdQh@wVvk|66JUcHc^{P|WdD(Q!8{
zZ!Fhtk!<!WKXx)b-x%uYAmU{m-eb80saSYx)IX7ILOl^ICAO`p){2juha)C&N;T^5
z!8+B!y1ufcu>Ct<e2l-l!H?s08Xo^po<Azj-_HNsn>6hI!LU?C^40g22J(fhSq_Q{
z*E*~P%Xf`>?Odn1=mJ(_{Y`=$ox4A6Yu>Nir{He?5%j7<*SDPQsk$kh?`+^A<Ag6c
zffl<#Zxxaf;~Q{5QnZm^q9Athapp{owYMF9d@%XVdOJ_LR`+<a?fSUr^l1s+hGG$-
znJwt7u|3dM4q{8`WU?{%Tle&UJ-xv*o)av3n-g?jm|OA`Sj}$eR7sXHrb3(<)>gnH
zY5bj~_EG;BD}37(N9i9ZEH~O~82cU%Z60%~6bA^Nx4M4QZ?9}5`nQL#oJ?=h4@$-3
z-~5H_urE+(ECJ>jCn5)cJUoUx2o{_vL7amsKi=HPdjMP&5lIhpS@>9Tfpufr{|6Aq
z(|8rwVBo?nQ-0tGz5CL#%LG$&bQ_++iZI(PMr8~GPK?^oe{B~HmA`#@Yg--J#|M0O
z8+!~J$Ot_4tVQTFDKD<CS$b$pR)O*%AvPLzu3KvDqkvm(-#V$nSUM-oSS%--#V{X%
zV5y^Nf1wl-Mx9=U?U}o^9JKe>Y=P!bzkPnw<=?u-11_@P$x|t^H-=OFEJ_qCryZ-(
z7b@h`*Bs0IkEAgKiba9M;cn0qY0*Za;xPhCwO6H9F>GU!Y{SgA!({F5#;8N#XN~Ab
zd6VgrKIm4DwmEXLKvA_YDe)1MuoStn6`>&+kk<3!ZwdQP)}+4+oDRNFdwo8jXo2op
zznWJVk}#bq?BOZfIt>U?LwG2oOKj>wM>LC}#bM=APHjY{T|;d{8i<|9YxKjAn2rm#
zvxnGW-KG0~5FR60tBJwros}`FL)%)nJ@1SF<GFMY)e1bmd$O}o#Djzh+jx#P?bR5G
zP%stSlb*Kf(b*v=9u2$I)Wq0TG}zl&w8N|}y~4PPbS4=gb#qBC(6~vtjXLplc1tg2
zHsyP9HGskwZJB7RGkIIP@umL$7K3t?O2&ZBciUY)L09jn>G2>^v6L_s%F#n_9I>%4
zKj`LnKN1ymeZI(k2u8C)7vZv#R`_5VZH;oHFn@KwV}u~xS(zDXv9v&-#-R?TwtG37
zUo5Vu2+M`k1wofd>q}>TI3b{{7?t>wn4j1o_RL%;P0K-BM}qx|F|TH))^ttJ^0*Dt
z?17zQt-e@ResheKv%nYbpT_G>`o1Kqb$SXsyCO(BjbcvVL|{CX8QaM9l>RwPn=Wec
zB74B9nRc$tuH+iORlC3tZq`<{ltm!k7j4LP;^WqRc5s^PM^U<4Z-*ms53Eo`aWGmX
zM+#4=3u7IB1_P&XJ%QUP^ZA*ePKJEfO$U?ySqW+op`9<_2Tslxc=&|m$CIyWX4qiz
zkN||qEZb9H#Yw*oVHYS0?zKQS`l;|^9#Mgmw=@-0V9zn%odq)fBvrD{W>%BlyR9Jm
zRU}8dpS$`OOOGRoVca#G7R?J2L|X%Wxd=q?m7>O2>f%cs=8>#-+6xoh&tXoj{E64K
zn2}N0f8ys=Zx5nJfU+FH@wTgjGNJmeB?zniK%bU8xOcO?5LX`?{NAusT(BhX`6Q<}
zd+;hBKX}X(NhnI&qjY2WmNFPWvcq}%Pp}9$D%taL?U2;uR6qsFj5RpK2G9xIP6BIS
z$HlgA-+&qcN^g__eB$f42iUauUF-5xS{ydk>8!ne?Yn?WS@rzOtGws)E>-feE@3>;
z`}68p9<5i{LK@7AGwNh^9$ZXZ7}s$3$elx8D{l~^*MDbte}~673FdIK`5NfVXBapX
zp)g&0O9c$=8D<!h`g~4h9Pp72mo9H3a?-`y^?n*xe?LZ+I|#H{W9Un(yRzwNNv1=)
z;5g=qGKZ2_Hjh8ubR2<Fdm5t;Wa2&OtX{l9XLyVYUd(1>a|GW!9L+r`H!Tm&O)Jmu
zBTcc#V~NkOVyW~_@8Kkp6YsR?Y|fa&7g*#XO&pK45(ta==55)8Kg;mt(K~`+@`T10
z+JU%lW!}z<`V^93y9tG`<+C$eBk}@`z##VGGhxL^0+OvR338G!iJFaY1s@$w_p}(}
z?FhMM+#4Vo(}fOt7Vun+B`|EN7IBA)SH-{cM196Af`8G;i;Y<+2+`AEm}D@gYkzP#
z7yYij;M9*C>f6T`>bEJ7*qTH(Mue|V<+E;))1E~Pc`YmK@8PHKII-pA<V;o$27-?D
zFJ29W^qMw%W545jLM7+2;FE~Ywj}|nDDUtGun+>g46!fwgT0I|9dy%hi2(1d{`OCi
zVbmojSGL7n)UuzA&<qm0Jz!UcZ_`(lgFa!8EZVs}5A{cjo(m3oKLjXp@Pd`b<@_(Z
zodQ0sXXk7v9a5j7O!p;TfRExX#ON%^OdE@K1C!V`-@XfauNz;qLkxTLb<pw#+ou=y
zY;&TR`<d+Ll}o8LhhG*Lh;{HGC#cKxMXFNTn}Z6mV#et`#UwWrf4EN%yes2<$54^p
zB-zc9xOO@&kURWx5go1?2$Is{1X%o{zQZRTiIrx##4mc)Fzs3eo`WwW<kG=F@O4q{
z*W*hhN&A7aLf7J20>@xDIYvV!d~xl!<3t|T$u#(So81d5g{F7W+r1=imSI^t=S?Tq
z!6hJgB+fkCKKJ{dXGeul#iTuHrF4A2?QW4#O^HWoin*(ZegCKNzKrLBDImSK>$)D9
zLW^<PlH}kHmJxfpiSUsqN$sJZ<ymEFI>@m=(Em8$vmwkli$D+aEmTp(texnnFlqf@
zjQa;Y1kfihzk46{{Ar2kVVe9q5@4)g6uTN%98L-Q-nX5|vFk9P|29PEn3A=a+O>xo
z54I`pHr*Q=QzgkS5sV6lKEFKj_e3$3gPOTd`araBfr~c$Yu7tRg)~Qwy!zUs(B-#+
z0I$u!$Ky;1NcvOYQX@!FabgZFDf(B#HhA${lK+S*5rRV5PkOK<Dc9HPnwtjxm?qL*
zSJ8_xsN~ZxYP*?zI8lDhh}V_5q31eNE^flCnL!Mw8c(EMwxy5G>rm8?YlcG>vD3UW
zvZRxKlS|n<4aO4R@f@VVwpzP1`{CkBl?GtzSAdO#GKHX5FV}qB1JYIR-JY2f1W8wE
zOGGrBl>Ntu0#&I)E(KgHI#kK-gy%GvL{!J4l72&~wwkykfNLsZz6(FbpsSf8y;QC`
zUM7|Fdy{tLh{B?mmjh*(Sqhm8qm5Q@B_tI}Ia>Qv#i-Ov&_Se1hlKP*j_Nd&;198i
zY5NV%k@5PypmRPQ7P$a-q7DVB3G$C|PVnk30)ze+6Hb$#L7Sz!<!yn+E61Xu4>w3B
zdg0Bi<ah?Zv?Aw$KqDK83?y==N{DyfB{N?r_4niStz`blluyhQ8!qWJO#OtKG4m^d
z*EX65A2j0z2)6s5zcUe5oxHd^M@)`fsFm}`VC&BeIY@t4Z?EjEY`V&83;t2|`c{D<
z#B7THesRnpCuH7)cQLM^Qd?y7A97eFy+Y43+0MKF;y-}ThBnjJS_>aB@<erlgtGz)
zOLHzRruuO8gYS+SR`UN3>ZL{6dwpgCvM&KX>*1s<y!Y#iuA<+1wRO^yXIuAP`u=78
z<W)@mlhDb%Ck+j{axVd`r+MxT4S5c)fZe1PV=<NX%@q6-gZExicG^Sv{`-EMc+<me
ztlGk+RPm1=wGqZZ7JDH&WDbpv<US#*gdIHAEm7oKwA=k(n?{ipTTFq?MA)EnZbwfs
zw*>aBA>`we?g4I2uwM)4gHE43n1aA$<N_bejSHJJr$yY0%3RHo2?C5r4J$#Zx6xF$
z=#^Ga*&I$r|3flmpIxRQ3;Q%so+<lYlOuVIp!T6RGxvl`3g~0Db0S}42PI^Uc&Bgb
z;j1It`k%n6GlM=R=Keg_RXgBc-C${3_fN6<p7IlLH!o#jzi+uq^0bD8K8Cb@XeeaQ
zwuy?|3Qcwq_pcs?A}ZfDOjpT!a4s^sw}`9_C^q;qNQyyiwAz=&JoxfmDDrzuaGXsj
zG3SdDxl^ooEH<`}{)fb-n`%YXWs;v8{1#0G)zXutp<qTUf*Quh-~}D$weO8Xi0q~c
zIw|yyNsR+N*xp)@x`}AJ)8BHv2l6}iuLt-lD}_>a$N#+kIyH@2l(@n<xkd<YI>;tC
zQ(WCD9Yb+7U%6sg$(2eqm23;%U{5@kreW*L*YEZZh&j5w?ed=xYwMx9VhA>Np|IRA
zREL}#ZfhM^M|V04IdXdxhW(T(_5uXsE<Y1J6AKfoS*rOsDJ%c<9W_wNc3PVka;dA*
zY$z~)24#94W8f%6jbI1{X~xNHg|FSqqt-O9DtS89-o~xIeo{h9zZhr1e<4WIQ%wM#
z@3JVB%Ev1e81^fpmMN}RPtuJXrEQOhV$cJty$IMdkYH(#pf3s!0;Lxex8O@DgNfPM
z%eulzVGy^dws4~V{49OxckbAH-4RXz3+w*TLqj6P1r|LtC|YWb7*uiDM28b&zMeDH
zkd~yia!1y8ZMU(I@4Z~h+0^a{e+iS+F+9<W&V?7>)(U2x|CQsW4ZxPxEOa%WNTeje
z#BEiEccB;qv_Gh7PyHo{lq~pw3>IArpO6L6&(llx3Hq=GXdl#q>GTL748OLI^9V<W
zKZR{j7@u`J+!`=C7>l512V!3tUfU&f04(FXtzPsh<FtJ~#%LeoZ2?HHDvNq!2v*H*
zUP8w#Jhl4kBK39NqgyU&w=y^e`2R<bTdS?rYw$s?QFW6yzn|8d!i2a-L5FF$Tl(F{
zt6)6stm{ES$@((=i}Umu{&9%%^9JBZug0k?qSo5iNZlqgHHxN+{wd7ysD&i4eH```
z7uWT;Qnbo}nArBm@!npk(1o9Sk=99HI;#anvpk?+wF50k+cnk&-5QZ{bS_&q>)2z-
z2d=M;`j*1yR<>D?rUF*%&#W4`J&*Txy}Nc?V4hYFfMB#Xy%4Y$5>e+SK2my8vGdJ&
z)nGpB!#M?s*@Eo$iRF<8FTE`M!!X`SYw)9_*_)!}@e0YKWgo2)c1tzq(k}xsHpJ;C
z<g+8G{Bm16>=(cFzx86B+J+SO*J?^uNnA3QJU<_>tY5Lx-;-CdltQCelg5kkA_;TP
z4%<4`M7HbdvNDWhzr*D>HyO%QzT)>560r8~2PXiVLRw#%T#gK!Pjup)<u^UrQdSgL
zgW`!PlPjj5tdqmDgIcWG6<&Zjj{}3<4`!JZ16S96Yw+d`Y0t+)Hv6XB)w-wZGc&(k
zeG+8#PF!S7#rt{Ocz~YVzOqnQ)(W1$7`tq{3@y|e19iqv+X1n|*7DTjwvNQIBvWtW
z4!^*A3y13I+iblYDcG0b={Ru*%3ozQFq5AE8FKJC;XC@6Ww^!mi`D}Bz*nnrFYtaO
zAtpWNp2Du;2_d%fw(wLwRw!cju&t{Mz3X43mn8TEV-3ws)VRh@$jtW04b!;Cmo4Rl
zMAlA>!P(Bt@=^|2oQZ&gXSs(~6s~D`M=SE2-GQJtAV~^_PYmF0&@aZ6B&Ss1-rC8}
ztfXM;*dbl|R{Lv>N5I>K!hnS=^ABw=&c#(e=gjU{GpC!A)AGN<h~r}Tz3DAkFfKL4
zKR*TrVJFSa%*@og{2ybg6ukaS!#0Y4b)Ujt#a@#FGuu%Md3V+szbYOcM(@>XeFC>|
ztIoyMopC9>1w+f<1<@jW^53Kwvw-OaO7_|4FU$rajyG<XHh_AY;rCsq0|&Ibfi#fR
zJw*x>=3$A;CL<j{;2)9v?M1Ug{j5&GGazS#q(?h*Ahx6vv0D_Y51U)_4}@UVKe!Q5
zz_L#&kNRjz@_7+2T%AVKJW-s-hYm5a6v*-oeT)g%@)x@~nCEt?i-o!;VH!H9f>5ve
z3hfgt#?Ffx3+%>NcQ5l=DnNe|uT@76gainSLtZCe8=I_XyJx1Sr7fRO&XJEF6XY^x
zxlqFySmv^p21PD^dV-9$A1sN+R~hpkl3$zh8f_225iu&vf!K!4#BOsUVakr1?_mxR
z&!jiham$s>iD#8U3tv3Mdqndvo#{!rBH^o|5%D&!pNC)Np6RT%QH$&a;WT-%E2gEu
zcYn<3%n36io`Ly@u8eq7Wr}&qB91ORKgfV4$;Svw1QT`YIYlt{-qxRZ?_U!l$|Tpw
zcO5MZ*1k#CROfzslk17_&$4{81Ekk>-rykAVc$TRzqV{_4SoY`lHJ!TdpE)`c|XL9
zPY{V8!VtfvuP{9hpS8H{&ch3s!m&>DW&_JB3jFJi5H16T-%7PWwq8mlV8|>brdG-h
zxeB5ARq2g5e|OtF{IZ6wJ9;vz5Ve!}B)#)OsLF-^If}Q2jwViyMog_(wyG;!j%0dz
zaumsa0H^;TmjiM|tW8<HB#YrOx8FZDUR^_8&Ttt_lB9h)ROA8dl>J3X>x0pQAxu|?
zDJ*IxkvS~nh6B?Y6d;y}QJu)&irvMlW@?Zhw)716!IOX|&_dq86~{P0oPo*?g;gAS
zyI*V448hpD!jJan?V{*YS*nq3Z6zw^g5}Boz?61!t~uG1oYn2`BF<@`U7>L=wB!)k
zC0P&rdL}pd;)oj(wIk^_9@;E3OKH2lMC3;xQZC7_^fmX-gOHf{ARUP=LU=YGWcj_L
z6G#36aZ4Ea_8Jv4%=+-F8yViyI@d}~L$s>gXIKx=kGCj4A;yV3r@A$BmLKFlG*msA
zlkc&9$Xl8{^h@aKBihkPEjPJLNF_QK1Kb)^rmaRS%n%{;H1{E2_o|2FVQJtC>ISCI
zMv@NcYm8W`vXnfvb;|2g;l!EF3+a73&7Sp*tJ5l>1ZJs`cPeUJT`9>R<?Vi=#bO3)
zp-bD6VIoFMeMiB+SlS|j1>Oa)-Aw}%R^Z?2HEdsLIM62CbzswdD}oF}BJ?-V`YjX0
zNwzM5?}K>ZMXn1;_2nww_I*Zo^fD#-u4k(z#ITC}=<TBoG+_@z#yUCVA{N=xst8$T
zu!lOw`6VN`JcGz`u`g$kFYJ$&27i*}fo^sr0{@gGr~4vaa32Ly+iCAoeQW&+!A%C<
z{{9<t!x3cow}=NjN2P7EUbQklrVkT5vOq!$QHU1`hov45^A~D{hm=Fq1+^bpob_BE
zMI0ONny-_)im785YlI-q36@OenK~H{tarNKQen-AxdQux-|KxfQ|BD=@#^i|7)LU_
zFMG<^R9X2MR!SG7`pz|tnA?(XEU*?BXCz>3ss>D)hknuv4f@A@ptKOv#%xCZjQpkr
zE5!oLl2XhogR9VQ-bW%lT=L(OL4#fc`D<!=>YPXT0yke^X%BLkFCS5?HGN)F<H#F3
z&iEJdB^_isa&DbB&PyeXT2lnGVA>{4LGXdNAq!nP|5{-W!DY$UsaOlV?fjnt@FMmP
zlKgyh5nNZXa87Ah1`QE=`x^RCe?cz=2t1{2;E?^hk96_S;GENz^Z#nF1^o>0MC{)H
z56DoUp%A}-|KfZCe}(Ns|1-pwM~C)L%sf2qKKD=coq^(&A8=}7R<qBP+7v<ufY@?|
z_pLf)_ssX!44UR1*0kD86oCR_o}BJL1s3hxD@Km%y>cZu#4w%5=Hn5Z_HM~}@Y0J>
z``n&vW_q{riK5K8johFLkblbQj!uS<8L<{VoDu@bn~E{SeMp_=9&J;^s-X&<WQElX
z5_QZM6#`|fkS{97X$*_;0f&Z{%2FyLy!uDOhLEUnj~lL$Jv%TAc9akp3`iENZp-Yo
zmLf0u+_YZj3C76V<v~WH%&e{ZOh1$?YtE+U|Kbd*A9i%J*2veQ<_!(+@T+#q+5okq
zrRG1H9XIwPf+1^fFA_UA4cM0Dqf&Ry!*O(egw<O(3Xa>TL$t<44QFv7j{GrQf$xnQ
zuJCjnXOq4_f`$&dPg_e@p0V{#v}p-ZaBsE8jkK@#)mfG$@-}a`x!ta?DA;FKAFNbX
zXV58?$sFi+)PND{YWv3QFat9Ah`cuOlMYZG!Qw(N5(d#yTT*p($iK`-vC&11%h*4n
zKQ5p>jeblRajMNibZF5hM399|4M^-=vqp~0jO(j)2r{HlbtJm*X{o|ygEf#?|JcE}
zA@2QBX8I~2w|9`xculWdm<$G*&b;W@eIOrasUg%J+Ql-IV5yPL?-hPQ%7l2?Dff2A
zerWU#Wc1~bebli1c~SoH@$r3Dy#C<D$Y?#Ij#K?Sy!~h~g(L$%!gL(!4n3pc!{MQ*
zx)PKf45lS!llp4~Uvf()2|}S>X+@(2Q6U~4+F!3KFO$=$!uA5$b?t)4jB7q5trj`r
zWfCMARo*<J5OX_P5`@>TG^Wk&u<_KM+{S^q^7P|T9+$&XvoxUGf`4WrmY5r2L$|#i
zw^}<+l6c7y>_i#UIgR|FlIXG;2eCHvTSL6Q#WdKtTFE7#b5k3WgiY3+3?KFQGkOEn
z_>i!bz{6$v3l&x`UA7?n0fhupGmm=ApIgw^Q3AV;wJ&$%NAH%|!FVm7)FAwQr0mf3
z+<$FoRAFB6HKaFoQT+@5+wv{U#EvZ2+7z&wKjjF?v*5`5FaF?#_3GH`OcWzRnaytp
zlDv;%7mp@rKUEf9KRc1O#EXKujU!$RT>-b%RRkh&C;7iw)V<IdfO0>urM$so7@w@^
zUf!NhcsGC~-Cv^oE`t00wYtLq(poMBM~+q`NCbBPDG_P%=E0kp=iVZIeR$k8^%dqe
zC0$$_?z5!ZM#e_QNvBH{g1oa=LG4cn0CXDG2{9QbA>7E#7^4K;?QsqJCP|!2CQupK
zB`bZ=`B3!c32Zz+u$cE(L32pLq^z`r^LIL(J#QIu9qI94mZ;~SWez_>b`>XlJ+YYa
zxHX_4$F-eXX$>8HH09p25G5tMIbHLFgk4uWy1V;_q4#7=KjE?ATvj0=^RD8g-&!wE
z-I5?s;NN!fN6H3_oqccS=(9z}YhRVHaE<94<K{f{awCT`ZMAR#5>ilIkZc*s+3PTD
z2R_2%9TKsKCO5=y=bw8Qb#|g6st}d^Y8B^#YgcjHmmb_p^ck<}1OA>BNY-s3m@b@Q
zp9+J1L`1z<d6a?UwME7!Y1p(;neMi3e9=^o4^}$X<wq!Qnlr96$x0J3JUxm@0qrV^
z!mb%QQk=kjDKwkiz*dXhA&bbN;)EZgk=eW1y-kH=;)a6zu(9A<dR>}QAv6UG|ME9`
zv&DZktDFe$f)j!?qnq6WSJiVSx!i~K4C{)Eja%xF6urFqrt=73y5QKobQHArBRNdH
zeP4zG13>9791RZb3tpvHAk}#!Z`O0d$b6^b&cE=2>7*uAJ~lM4jD?`5@<ZOYa4Wf}
zU_z%u^vD=L2Gh$^`|rLrWoeE`EBfw*LVs~VH3Xn0oO1iBrF~Ms;TSl=84ro6mWEy~
zg}AmU>=SaU9519Ph8SrVvkh4aSugPd%N^i`a9Ki6Ewb@|qS&8^vF~+8S`3pm?wuZB
zZ0>s|Cm$^`UgtT#|8;;B^=h-33&`|k9a9)FEPjd&y6hNw;#Y{*4Nsw7o>Gf3HH);~
zr-C6pNXCA6f;Jm7Z`gpX#~9a9o#xvc?gs2WRI2Sm=Ks3X_xc4;h)0Ly+-&3mv`g3`
zEcu`>6GUZhQIqclfX00eggx6LA_;Gwc6xQY1p9Bc?u0NqqtN<rBe<CH(`6yL7wBjs
zI9GJaRhNXCsjsoCv`TZtp{T$Dd5X%PFXg2z+|@p)KBJMomZgTbYd#>I%Nd)A!edKR
zP_jhzyadIk2+G^5&Nxe9&u|9`VA4TP;-P8AuyA+y2aZieKjY3i*}<onVpayS<LFl{
zYbUU9?OVg?XEPR`{^$@Jz71D~hks#v4y3u>uFVbpU7ORtasl`5(C|dq)iIVS9v;xL
zelWU0xN<p||G=J<kj1z|i`{+{toKrHP$c;!C$dx}ImgK;6R$}3hqhW1_-nmiWZtE{
zN}`0STOv<4j$2kRX}rg7?Fxx)WWOp>04#72%8h$f%iL!_?Sk&J2sVi<NgQ9~805Rj
z+ADI*TRKe<39X-Bg9LDLe0Ix`WbWN5m0F-tcrOAC^jxeu>5*Olf2j{d=0_xwi|;+$
zT540r!UxY9&)f}Nl?^s-C){EcRv=Ui6rNo|cH61-A`NRn&POv;5tFQ>8Wrr8a!`a`
zT^TXUxi001*<`2Iq9C@C4u!{?hG`Ei!BvYXN@p(S{*^lPIqw&8{O%8md=h;V>@hBZ
zE(;QDXDwu{;vWQXd-S1`jVEa*4=}-)SpIWG#^xboaGeon^q#}I%b~$+xs+12nTU&1
z^=hJ*-(VD9`3vCTRlBq7(Ik~osOj1-Dql~WVm1v5y_hw!_FgIs0JhgIDrPMQ^#>dN
zF|f6~W)Bvpm<il-N*?fHerb)~!N`^ycP^gNis~q=$de;fx;?;QZKuwAfq@VrbET}X
zH<gRn+ZcA@7wUT-bHeyzg;e~*%7oIgIQ;d7U07c1<Ep(<{WO<Pua^vr#cDq27+T6Q
zoT18uPh{(bD<ZbY_XUI&H3n)(f8e=r(4Pexqh{NQdb3X$+i4+e(n&)5J-l!>s_`1<
zoi*0=RF}4c=oirD`$@JUVz{s-|JTpArUpuaHo##5S3_&beb;+`z1=wd2?VG6tZZG+
zXZ7h>AX<q)o8#7UKQEF5d}#GOQQQQ5;Xy!911TizHfd(2f+o=17L?evS1n;?1VHC8
zz6;GPrKQdtMQ;Fd)$%ufc3wx7KXuN;0Zee>NHr)6nv=oxUQziD41w<Bw&z=Gl&sGD
zVL<>6OPc{W^cGVU<6b7<6P6f9{}7NEEkOmM_!j2SsTRc+NF0~}QY_l2U@;(`R5$+B
zSs?0!r5*0YZKr3jDEemjKCIL}(Pn8YB{0fk3fcXSpK<f5udV91rE4~nsp%~m{`xj?
zH};T^$0;NY{`j%hlAaJ(6NoaUf7UD3u(I|L1MtCVC%KE?BWW5elC}eP(|As1mHc2t
zfzYIM{=rMpKyCWBU=JN)K?)BcsS+0<h662bnFzYN+m-1G$HbWox;0;rp9nnsJHx+I
zq%qf-?*zF|Q*GzBh7lGCqQsj!m^4l0tADr;M87jC73#gyi9)}zZw>27EjOD=u@`)|
z*`>0Aiz9t79$r`39;SWpxc~Q}Uz>E+W^a4WE(JC%+o&QTk~dCqvfX03>#LQ@rBU}Y
zd8Zr273D)^^9gBA4!HUULsw2+kk3;!COV(^RbRwhRw+)-9!rsvzpiW5{mG=1&Q{)y
zXB|V=dNl2%rS=>&-}JSYRyPZ_+L(=o3iO4W{#{y|g(wR&?eunLlvLOJqI=0c7*zGB
z%;i0FKJ>aGgEMV5NrD$u9vOZh5E%WST@v@)bVh)r;A~~>cdrb2Ko4K|IGnY8rzcL^
zYn!3xvlkZ?OU-^!PrFn)*@Q5ynU&tBmE=0hw_wvSE<SiY6UmMWd(&Qm$57^<s^7Cf
zN;)&^lX)Yy7=<ZbCUDYi!R5Ry|6YdRi0CPmQl}?3qfPeKzrM$p5E(@n(lnR(blBK;
zlo-1<ddVA(^bw>|b(@V9XPk0{Rw)`;a!bA=rvz7d0Tg`vK!RM@yWA(d^wVrlDAT!a
zuCp_3dQxdNI~o-X38Q!4!|+*`CODQl;7)jZ7@M$ED!mltc7@h22QxDtOt1(sEz1gh
zo{ahVq4lV7jbCB+ys!pdtd=@Si)fdUAiH~lx3h$Y1&h(gSxH;}@_F2Ac~z~bAbpH*
zuN6d_>W%sa3;pTDl0y&u2^f<0*FrwRX%SDre+D9=b}BNdLP~C_78xT8DIs_sFCjHQ
zQtYX~E{FKoV1NrqZ|E88{T#76&YjaZKaTpu?5=|Y(C_A&PKGXXjSen02M4+r(%gqd
zLB`Av17{HZ<vsUsV0)Y7Ic*mK-%Q9p0GusHn~aO-5bs#W3<BNws?VM+2TSMet}9Q$
zoC>~A$qg&q)LP~L)xX)BP1$jRy9Uc7_~-DRrU=okIKhVgh&sfyOl0K%bHrL%RXN1o
zy2Su2Iy_|&G-TGNC<Zqoeh&swK-cb-^NB%EvgHspD5cax9mG-5+|kR=V*#`6u^M4j
zA*+A7Z%8nmgU7_1?h`N$mYQN0ZOJFSi(9YbDX&oQ45poR@+Xghw4HaVw`I;7v<4;q
zTjhj1hL*2*+^c8$-{f#X^l_fRT9ATBTdo+l$~l}PzIz%)v^hl{9y+D`SJJQEf{QX&
zlc{!lbZOW^*A2nC%*qBl$7?%YiE(M{!0y>AD?yIU=FFI_MibDkgL4OI=Er>JKK;jk
zPc-I<Hnfo##b}PQnk9O2vgK2BunKbL4QvD$pWU?%dAeTa_eWryxuoEO7Zq8+&wqmi
z8=v03DOA~0ByDu?5eIW^9c*ySf5aD}W+iNNz!@t9HsFbVS>-Ik;v_J7v+P>AO`Q{x
z>pr?6%z3FrMmid_QM%Q0_$aX+o)0|BR)xAR%We6qP?xs#65jOwwIEwlH(g~&=c(Zs
z$<8P?T4p<6RgKJxA)H8|#xf4vSQb6UJ9|FnucQ`a2sX|Q^s}{fdw6_C2?qViKl{CT
z?Ri?maZ6-$jci{&X<Tq82IcwpK>{4p33+uMrUK=S8KU&z?94f!($?H|r_Kg8b-&GT
zK}>E6IrpjU$AYzg%J(NseD1q!E)6nZONEm^LNxvFtEBAMT`R+cTm)`N>Qd8w14bxP
zpjd6FlCc<g7t`Xoh0yw3Ki*r{kKw)e$Xn1(iLj%8m236WPE)+qmPE5D<aO>CB&R6Q
zM2ESQa8zpjEO)mq|IdhJ`|OX4lxY(bRbPVh1?|-*-|vWcg@!kHh_^5N{@gc!$N^J0
z@{rXIoT1cTr{C5Skp9=W%bA!U&cA@Hw}1uh-PDkU3iV{SN+1_>%|<C+v*i?@M>jme
z4L1Dev!FNLO(Ad(!ZoYDX%al6Frwg~=1%=Ruu$(LxBbrTbMJR`^jA$!02AKB)>--u
zKc^L6ZX&Y{^4jaPz@7fD-uLPLpDM09s)=v;TR@~&=@1dV6akSc2rEs4AQ+Jn5Tq#O
zNfkm(=$+67MHE&+ARqz~q?bsOA`p6$-eTxrAaq`Q@BRMx{V{jX?w&I<cka%dJD+p+
zW2u}+*^Mq-hU`7%V`r23WFG#{GPwREn}3&{w7Qa@kLMam)iqt1&FnqCepSsLYwFop
zj8P#kj%wFsQ~a6aT8FB#_PSVoSO!0@bkfvOZx8z@-)xRFBE}+>6Sr(<7O3ajtfdt9
zXWfb?4HFNMh3WVYxW#9b`6(88Sm7)p&0mEjvE$VRg5M0WOZtA}L(XqD2ll&tW1s6@
z-j!f1_C%hdy1&kUvr+afT*i?XU^iKNrAlCiIk^Owqb6sTixa8*xmun>)1PkEx$f#a
z)Z?jO@a2=6kT327sX|aBTg^qHt~dilx*?-RpLWHwKZB>^fL|t)rg$NyqpLsW5T<1U
z_*cs;)rlAnmn)DI7@#a@VrRGr-TUx>_iUu0caIEUoM7XNk{`Rr$5B?gBF2NMa+R!f
ztLDjXpb@pmXD>Jc0-~3}2q)xx(_^dDHyn%)3-TR1kF7t?s+(#g5ojw&B>clIJL<3$
zo!?KXwPXR>Fp|ir(S4Yf?O(y_z_M6X+Gj=wZPX)i-qOtMk7kXSw&A<2GT2jwMj@0!
zS@C3~;G-p`U`tpo&yO=pn6^uvr}0JP&W95D^GcR5-ep9Tda)zXu(Y)O*)k04R&V&8
zG2@mpZ`}Ic)o9kPlmza@*P9;?bjS=#uhkWUTIwbyyBK%dW~X0VJfj}7QUo}rGx?7F
z0mtH@E*}OrXY;xVbpCk;3b47<0X;4T-Ra{Xp%u#@_oPI<3Vi<$Qc1C#rb@t$JC3U&
zdH1dgmNmG@(EuU2syo+H)AV6<aE*<N85E3bb5Km9yxy}1ils@pd4?t1gx$3@Srk5M
zuWKqL(ku-#RIW(OSUvs+2>*0rXfMkZGJEnn$L$i91v64^vl=-4QY9KhYL;M{9=xfL
z_+;dVDA`IUaY&MXO9>f=qQWtoOhWV?*MaeOo(&~QRl6=e{!S-{Ml;)q^6u?hh5wzH
z9X|aXlQ_lkjwb-ce=f=;)-r@7!V&d9S%#BxTM-cv&ya_X49gx+D%<S0;C%b(O5Y!y
zYmg-pL*j})lI@_N026IxA}p0QIEdTRk}PvIEfH(8X8TO+Y%Wp)`>VlpawNBAK^`U;
z`rvTqeTR+;;D49jUR`v@@F7epfleVv4*}Dnz;gufURM_8x3`ueAZ+j>16iVSD-8^0
zb0QKyu+|x7(q6|&w_etg0BD=f6LlBaa>cT5{uP`x?BqztTl>c7b|>}12dLUlGUnX+
zw}O(ty-~C}gS*jkk!QnE)FI(wK{{_k)8~IpM9^OXg%ozNUR3bvdhRnLG8sNWJgGfR
z_8%lN2H5ZFziz8GY6QgzH3Ye?435Q`Rk_Z}FU&>hmEiQreUIg4{G|G1<$I2%d$9t;
z{qhS^KPaC>Pgn7fp-iB{sj{7skBHHaw{b2%mC?)vV!;PakXvhvC1iQBBX!j}Ltq}E
zGK-@o4-LAR$J<j1-l(tBNkhXP^&^?x82&i7_@|ez(iq9bVb7ZCf0I<L>?}rfeq4GV
zj>--8T1aSS*v<3^4(EO#NCw`uuU>H!cI<iY?X(qX+A)3niR`Ek@vl`T(xwf|-{X9)
z3#-c{gKMh0Ua6%h86EUZJHrt^sdwDh$EV|(1Q0Ngw)ACgXD`c2nFu4N3E^y^8n-cZ
zu9U!|@>Rb-KOms8{8l)_rp)6FMLQ<Ffjap0l~}1CxLnL;*VbWX!<EOxP^m8JDqEq&
zp}`g5-Bc|h)n9Te75mre%Wbz@ZR~!oJNt4GfsWp32Dt|HqOeZPFj{<5pLU6~w)FrR
zhE3PkDkObZiUAD=jH<8c-vt~u$D2I_tP_ID#%AB-gIP78&z?gv@nmUQejs9G-HDiS
zC$8*NyTS2fa~JH$X8<Nqu%T4=m~!u+3^MSw3y{*lV;2-0m^*y#QSvgl@|y_$?NK6o
zLR8bVVM7<~nKP*DfMf$6<XZ&JX}zJNvYR0rGxrwNg+vQig7xW&*IfUj#5`TT0VnRi
zkeA2$NFVl}Mr6>})9)lwkfNs!jL4<IN@z2G$xLyr=^&UX@TxwZw5s`uTyfCqv)de1
zRyrG|w}9EXLr0dnHzCBSgcMLi5h#zY$1i-F`QSZ1x^EC-#ptDhJQcB5b;7ByVuK{E
z9z9ir#t9FkcAWpe8u-6D*s^c1t6=av<73%}B8R@_-|BsX$>b@)ef?neXC)kFvTuXq
zQZEIL_3xLPZqyz)Rv~EJ8}GmuhI#R6%_&9v>$2%oJw5>Cu;RB5=3}K60iL{JCUyB0
z^-U2gZyZ+P$N`;x_ZLEyDdDLZ>}L(jq~~b!PZ*y3c;?zGvIGYB8!{t;hLFvVtc_x?
zTg|9)dzHVG^nK*bCLIH!$O9;59<dG304)BTzFDSD>QSirj(aA$^sb8&h)YA2dAE!u
z##8^~;+rXgQ*#Ph2XF3Z`BY=S;q<8zy>6FD`dgI?w2*hCNnW&KKEv{U0P9Rz_1JZ@
zij-yNNi~))ho|<>9%%mv7)<<eYl>kjMP*ue`(`fWVQWtl;HlO~bn=KoYLMQ&&(Kg0
z_!yZh7Fd`8ARDFm*DNGkcQbwAMI&VzGbfSFOPt7?Z+{-$^P4PiCmo#YmF)9%X}=ao
z_Rq#RE0=x2*v`B3*XkYzTi`EB(-~*+4Dd;v{4Q2f2%)42-sViK?Lo<V;yvJvUB^Jw
z{>=~-@tP9Wf6}@I0cWQ#o}0~<u#JNI9HyZ}_y$*4;=rA=<H|=Vo>vqzM~SW6PpwDl
zlu2InVsA)8Fs4LLB__RMM>z8gl3vi6%ZA?^)~~aY#Pm%AXy^B?FiA4Wpd^aF>9K}4
z)Gh&JSFFr6a4iud_wZL`^fBGmJ8B-4eSjQ&2v*tom0C6AD-|*VQ_Zp2V&cErMUo%G
zT>9npkaXmT*n;*~QV;u4(B;8eAdYl*Q-b8R378Wb=`H>QaT0`LR2E)7t0BkduK;z(
z<8oKPf0Jl1L&=1wKk&7~Ia&p>{wr_f-i^Wdq}4uoW`uzxgZVT;O2s25EKpEp^S6w*
zG}T|j^6S6}8T@u#WoE>v8@M>Ue~=?Tr0@VAvivu#c7z2TtO~QzO*gKtlGG})?^ye5
z!@dl$M#Tp@?(dvvCH}dly8kMOisZ#8=3`N#4KsBLnjiGAfGt;<Zq#REkR(6uuDU5<
z!OgO~+6qtiCOhC>X-!)_tlT`6hnOMqi?oR7fN0hKIcWIPT=Yy;{%}NmFWu_3)K+9e
zIm(rnu4Ui)YT*-{seO*o<Rjxp1P%^a<z2n;NV={etNZJ2#`Rz4jV-t0d}3Js$>F^1
zej!XwJ>y<Ky~KW~c8ZgtJ;MHXJ?pWC?S5lfQf#5@$is;W{~IG<CMlEr7P6`G0u)r=
zAb%A&ke7GvKz*AzDRda0Cjy;C2B(&tGd9T$8S5P*2HDOF(i=U78S^UsdwIpe(X?Pm
z(5Jabj3oR9Skh%o8T8Cr1YkQnOa6_vYu$CzMs#&b@S%4BZx>Wl){>~RWcXGNj()07
zBm^o^njt=HOG)P$bGvMj<cjmKZK9a^6=q&$Vb=)c><jl>d*c+WWGuPNdFSl4x8+xL
zcp^FhLldmFb@n6>C%(Gt7n#NF*9>(L8<Gc?m$xqMn6K18NI(6Wqt@!3u_hzi+tw|d
zJ;Tc$Io7QS3ogMWxsHeTTG@Rdl@F2D-rFtO*a(+1e)nzP_>f0dFWY6T^3>#nZ=kut
z)FE;gU}G`shnm1I^gZmA7Pt7ltJbqd>3+in(Tx7aPtEFCqIDN#d<-hZ9RtSqZYE0W
zrSG)Oz!UzOa&7J0BXk|KJx$rp<TblzUg(}l?jw5GH5BCh#H<OZILodWf?EZ+ppj3F
z`Ld*rm)-sgJk{%q8~wZQJ5RbCHfW3#C*-$`m1Oq&MkA18$(T!b;G5_^*0JO)ip7rG
zcRoymwD`c?KSxfb)Uppran-CC+u8U08zmt#3Riewn=7aJopUGMtk(IWwE?XmLTMPt
z+LoX4Pr(MPze3h}2%!KTl)|yv(N<p5h4Sk8*KG72gBoq@6+Ph<TWyCKLaUJ%m6Bcb
z)6`;YBJ=u5YdP-F|4$e~fLrYF-ek<+S9Mq*5_jR0Ci&Vzr`I<1)(!rxqc*q3fWrYM
zEq|TXnSp^Esuw$}=%>ZL&Fs)xT~oDf6^e~+XIL++p#9)(d=N!F=6~wVw_BAx_&xYM
zR{Bli(eYv!jKv3?LG;GH@}?6?4Z|MM5nnO4e0?m|Ff|ppXNOl`h~0Y`9<}zOE!Aj*
z8!Ockwf5~lt1X0j#{ZrDQ`vm1a`_$sZMeNfb-$~LDQYdm5jrM_r%gI^K$dRu69TI-
z=wO#is<KDYNpM!PudKi9U_%Sozl$8C|As=O<E#brlBwV6j8@u*3LM<36NM>P#y$!C
zvM@Lb4hjA^ulC=Vi>=|2M;jA})?m0R-%RYofWe(Ti~1QR7&v(?XU8s+Y;gC?R%WeL
z!L>0aCR(Qj>M}~d;xTS}3yP{9-s(B?AS5C))jzw<uKzlJ;i8gV`_oj+rP`u?-;Mp3
zghY>Xuz2>9Yi_NyN9EH&)Gi1MFSY0_FFx!izuUgbuHAo*aZM2?8NW)Y;DvF7;0gPT
z)GrOtDGDyQaakF!Z5IqZdTrC?yDf!=PUUtU>QV>DFU}J`2iS)bV#-P^U&lkuw)iEh
zJx%0zH!07(OgPjkDkUX8aWqH^a_O0`qybvz(T)Fl9gz+95>m9-xXz{asX^#z@t&B%
z{R@%^DK8c2#ZPvqYm}3`&Aku8hiiIx&?CbUw|(_|rY0bxz4;NIaN1dk0+Rt25q8~m
z*ViJQtoSBA)twfjbs%e9$lqT)<eZhbk5$?w_r(OUqyiIhUzPbzgoR-FeNv^|$ADN8
zCW68JaT?pVuWVeeuTq$B){62}3MKa@;t8~dL*<U^=SI2GG<KCMB(i&JXf@Zz8@@O=
zPoTvlGa%r*cpLc$M=yB7bWjxUoVqvym%Znu#CIdTpoP(Bu@@;M5v_cqHMNj7(k>&^
zaoeLrvAtpL?WEgn*i$yTe!@3Uh2%b#+#yJVu`j_jm1Be;YHg#eOApsVI9VXAZQiuf
zQjG!nr7OiW#YrbqM16}jO*4f#mFiQ0g~HyXKmiNHM;o=_>8J>3y5;%{n9VSp1?>LF
zgGZ%`6cZFqid;l3P4&d(Z}&H~Cb<+$+o;LNa1K$8fH5@2JX^Quq!5#7rJ4{3tmwEV
zK2z_8-hwhE=8SWTiEHEBW(ZMiu&^-CBVGwqzMSjS3(Z$KPc(G1SVna_?{7>PF}8x;
zX%S)DLyHw*U&C+63JLe^N^!sxE~uIJUl|%nyrfmQ?Wl5?6Zk4Z{J!5sHP)mn_;^da
z%yM-NVAE~vpqVN5N5VmE32PlNu~4`d<!6dbD=x*gBan7yoMq-{5G`;Hyl(D(*HG7$
z6|ReGQ2b19<UN<FYAydG1k1Pcz}9cxYgT39tyDB`a!))Kd4E~=tsLa`(9Ahl>+@6!
zEz}v>@xJIbCCU^mG!u~I#s{-)bvxh>^jQ2rmeR~SkmWUhv##IcMpH`iwzIke(?_Es
zdNL>{2Btgr^8mJm7JfEgXvW=CwO68BUfrM>>e>dsTEA+sU@z~3hra8yg%2$9j<+d4
zkbW;mO*CJ|!8aP&dGI2y^;7HWUmQF?t8MKo25|IdYyi|1rFDC+_`C6l-eWv)K23k*
zgQa)TnTT@DBu&*11EmW_9GSS;h?`#--<@+s<yYm1!QP_!=zy5RBh@Fi8&RULnA>01
zQ^XEJcaB9*n88q~y9%;yjAx;+>%Ivro$MnB9pjDf3D`QvDRZ0ts(pQk!wwbv)MBb|
z3~@&P1p)cU7$1flxH_?NzED{6Y%Np(;rvmhZ7@d<A>Fj3@QA;cX5cC+1+^i6HTzca
zXSxEmInboh<h`R!JgRw;@d3!<-3J~)38pUx6v~k2u0RCHxilf7c;sh3I<&(|4{rwZ
zb+U%$+?I?Q%^m*|^!_hw_4y9&`c8IbHHGTQi;5omnBGtrh!6RRaM#WW?i0n&{RXqo
z_oa%Jwo;gJ!Y^$lmb`&bhqJ`HEmLl}<3(ZT>b#`>a-iPqm_hQYqJ@5t?DS#(f51=V
zg$qY%a-+kb&10G*Yxnre8M@R5(N&645&i7VziDkr5jT-q+<;I#$WjTh!T~K%S3=-Z
znc2;eUfVL%;o8A571bnRjraisCP!cz8XF2pjY!U_gYvA95GCJt=~MDMj;<jg?^G9l
zAx`+Ou-qK!osti(<-6Jh_-=rBJR2+1<(L>b-#y`7<8!QlfV-8bxVWR=<L7@p*S+`f
KZi$vn*uMcj{I1LZ

literal 0
HcmV?d00001

diff --git a/packages/context-coloring/test/context-coloring-test.el b/packages/context-coloring/test/context-coloring-test.el
new file mode 100644
index 0000000..4c62d7a
--- /dev/null
+++ b/packages/context-coloring/test/context-coloring-test.el
@@ -0,0 +1,375 @@
+;;; test/context-coloring-test.el --- Tests for context coloring. -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2014-2015  Free Software Foundation, Inc.
+
+;; This file is part of GNU Emacs.
+
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+;;; Code:
+
+;;; Test running utilities
+
+(defconst context-coloring-test-path
+  (file-name-directory (or load-file-name buffer-file-name))
+  "This file's directory.")
+
+(defun context-coloring-test-read-file (path)
+  "Read a file's contents into a string."
+  (with-temp-buffer
+    (insert-file-contents (expand-file-name path context-coloring-test-path))
+    (buffer-string)))
+
+(defun context-coloring-test-setup ()
+  "Preparation code to run before all tests."
+  (setq context-coloring-comments-and-strings nil))
+
+(defun context-coloring-test-cleanup ()
+  "Cleanup code to run after all tests."
+  (setq context-coloring-comments-and-strings t)
+  (setq context-coloring-after-colorize-hook nil)
+  (setq context-coloring-js-block-scopes nil)
+  (context-coloring-set-colors-default))
+
+(defmacro context-coloring-test-with-fixture (fixture &rest body)
+  "Evaluate BODY in a temporary buffer with the relative
+FIXTURE."
+  `(with-temp-buffer
+     (unwind-protect
+         (progn
+           (context-coloring-test-setup)
+           (insert (context-coloring-test-read-file ,fixture))
+           ,@body)
+       (context-coloring-test-cleanup))))
+
+(defun context-coloring-test-with-temp-buffer-async (callback)
+  "Create a temporary buffer, and evaluate CALLBACK there.  A
+teardown callback is passed to CALLBACK for it to invoke when it
+is done."
+  (let ((temp-buffer (make-symbol "temp-buffer")))
+    (let ((previous-buffer (current-buffer))
+          (temp-buffer (generate-new-buffer " *temp*")))
+      (set-buffer temp-buffer)
+      (funcall
+       callback
+       (lambda ()
+         (and (buffer-name temp-buffer)
+              (kill-buffer temp-buffer))
+         (set-buffer previous-buffer))))))
+
+(defun context-coloring-test-with-fixture-async (fixture callback &optional setup)
+  "Evaluate CALLBACK in a temporary buffer with the relative
+FIXTURE.  A teardown callback is passed to CALLBACK for it to
+invoke when it is done.  An optional SETUP callback can be passed
+to run arbitrary code before the mode is invoked."
+  (context-coloring-test-with-temp-buffer-async
+   (lambda (done-with-temp-buffer)
+     (context-coloring-test-setup)
+     (if setup (funcall setup))
+     (insert (context-coloring-test-read-file fixture))
+     (funcall
+      callback
+      (lambda ()
+        (context-coloring-test-cleanup)
+        (funcall done-with-temp-buffer))))))
+
+
+;;; Test defining utilities
+
+(defun context-coloring-test-js-mode (fixture callback &optional setup)
+  "Use FIXTURE as the subject matter for test logic in CALLBACK.
+Optionally, provide setup code to run before the mode is
+instantiated in SETUP."
+  (context-coloring-test-with-fixture-async
+   fixture
+   (lambda (done-with-test)
+     (js-mode)
+     (context-coloring-mode)
+     (context-coloring-colorize
+      (lambda ()
+        (funcall callback done-with-test))))
+   setup))
+
+(defmacro context-coloring-test-js2-mode (fixture &rest body)
+  "Use FIXTURE as the subject matter for test logic in BODY."
+  `(context-coloring-test-with-fixture
+    ,fixture
+    (require 'js2-mode)
+    (setq js2-mode-show-parse-errors nil)
+    (setq js2-mode-show-strict-warnings nil)
+    (js2-mode)
+    (context-coloring-mode)
+    ,@body))
+
+(defmacro context-coloring-test-deftest-js-mode (name)
+  "Define an asynchronous test for `js-mode' in the typical
+format."
+  (let ((test-name (intern (format "context-coloring-test-js-mode-%s" name)))
+        (fixture (format "./fixtures/%s.js" name))
+        (function-name (intern-soft (format "context-coloring-test-js-%s" name))))
+    `(ert-deftest-async ,test-name (done)
+                        (context-coloring-test-js-mode
+                         ,fixture
+                         (lambda (teardown)
+                           (unwind-protect
+                               (,function-name)
+                             (funcall teardown))
+                           (funcall done))))))
+
+(defmacro context-coloring-test-deftest-js2-mode (name)
+    "Define a test for `js2-mode' in the typical format."
+  (let ((test-name (intern (format "context-coloring-test-js2-mode-%s" name)))
+        (fixture (format "./fixtures/%s.js" name))
+        (function-name (intern-soft (format "context-coloring-test-js-%s" name))))
+    `(ert-deftest ,test-name ()
+       (context-coloring-test-js2-mode
+        ,fixture
+        (,function-name)))))
+
+
+;;; Assertion functions
+
+(defmacro context-coloring-test-assert-region (&rest body)
+  "Skeleton for asserting something about the face of points in a
+region.  Provides the free variables `i', `length', `point',
+`face' and `actual-level'."
+  `(let ((i 0)
+         (length (- end start)))
+     (while (< i length)
+       (let* ((point (+ i start))
+              (face (get-text-property point 'face))
+              actual-level)
+         ,@body)
+       (setq i (+ i 1)))))
+
+(defconst context-coloring-test-level-regexp
+  "context-coloring-level-\\([[:digit:]]+\\)-face"
+  "Regular expression for extracting a level from a face.")
+
+(defun context-coloring-test-assert-region-level (start end level)
+  "Assert that all points in the range [START, END) are of level
+LEVEL."
+  (context-coloring-test-assert-region
+   (when (not (when face
+                (let* ((face-string (symbol-name face))
+                       (matches (string-match
+                                 context-coloring-test-level-regexp
+                                 face-string)))
+                  (when matches
+                    (setq actual-level (string-to-number
+                                        (substring face-string
+                                                   (match-beginning 1)
+                                                   (match-end 1))))
+                    (= level actual-level)))))
+     (ert-fail (format (concat "Expected level in region [%s, %s), "
+                               "which is \"%s\", to be %s; "
+                               "but at point %s, it was %s")
+                       start end
+                       (buffer-substring-no-properties start end) level
+                       point actual-level)))))
+
+(defun context-coloring-test-assert-region-face (start end expected-face)
+  "Assert that all points in the range [START, END) have the face
+EXPECTED-FACE."
+  (context-coloring-test-assert-region
+   (when (not (eq face expected-face))
+     (ert-fail (format (concat "Expected face in region [%s, %s), "
+                               "which is \"%s\", to be %s; "
+                               "but at point %s, it was %s")
+                       start end
+                       (buffer-substring-no-properties start end) expected-face
+                       point face)))))
+
+(defun context-coloring-test-assert-region-comment-delimiter (start end)
+  "Assert that all points in the range [START, END) have
+`font-lock-comment-delimiter-face'."
+  (context-coloring-test-assert-region-face
+   start end 'font-lock-comment-delimiter-face))
+
+(defun context-coloring-test-assert-region-comment (start end)
+    "Assert that all points in the range [START, END) have
+`font-lock-comment-face'."
+  (context-coloring-test-assert-region-face
+   start end 'font-lock-comment-face))
+
+(defun context-coloring-test-assert-region-string (start end)
+    "Assert that all points in the range [START, END) have
+`font-lock-string-face'."
+  (context-coloring-test-assert-region-face
+   start end 'font-lock-string-face))
+
+(defun context-coloring-test-assert-message (expected)
+  "Assert that the *Messages* buffer has message EXPECTED."
+  (with-current-buffer "*Messages*"
+    (let ((messages (split-string
+                     (buffer-substring-no-properties
+                      (point-min)
+                      (point-max))
+                     "\n")))
+      (let ((message (car (nthcdr (- (length messages) 2) messages))))
+        (should (equal message expected))))))
+
+(defun context-coloring-test-assert-face (level foreground)
+  "Assert that a face for LEVEL exists and that its `:foreground'
+is FOREGROUND."
+  (let* ((face (context-coloring-face-symbol level))
+         actual-foreground)
+    (when (not face)
+      (ert-fail (format (concat "Expected face for level `%s' to exist; "
+                                "but it didn't")
+                        level)))
+    (setq actual-foreground (face-attribute face :foreground))
+    (when (not (string-equal foreground actual-foreground))
+      (ert-fail (format (concat "Expected face for level `%s' "
+                                "to have foreground `%s'; but it was `%s'")
+                        level
+                        foreground actual-foreground)))))
+
+
+;;; The tests
+
+(ert-deftest context-coloring-test-unsupported-mode ()
+  (context-coloring-test-with-fixture
+   "./fixtures/function-scopes.js"
+   (context-coloring-mode)
+   (context-coloring-test-assert-message
+    "Context coloring is not available for this major mode")))
+
+(ert-deftest context-coloring-test-set-colors ()
+  ;; This test has an irreversible side-effect in that it defines faces beyond
+  ;; 7. Faces 0 through 7 are reset to their default states, so it might not
+  ;; matter, but be aware anyway.
+  (context-coloring-set-colors
+   "#000000"
+   "#111111"
+   "#222222"
+   "#333333"
+   "#444444"
+   "#555555"
+   "#666666"
+   "#777777"
+   "#888888"
+   "#999999")
+  (context-coloring-test-assert-face 0 "#000000")
+  (context-coloring-test-assert-face 1 "#111111")
+  (context-coloring-test-assert-face 2 "#222222")
+  (context-coloring-test-assert-face 3 "#333333")
+  (context-coloring-test-assert-face 4 "#444444")
+  (context-coloring-test-assert-face 5 "#555555")
+  (context-coloring-test-assert-face 6 "#666666")
+  (context-coloring-test-assert-face 7 "#777777")
+  (context-coloring-test-assert-face 8 "#888888")
+  (context-coloring-test-assert-face 9 "#999999"))
+
+(defun context-coloring-test-js-function-scopes ()
+  (context-coloring-test-assert-region-level 1 9 0)
+  (context-coloring-test-assert-region-level 9 23 1)
+  (context-coloring-test-assert-region-level 23 25 0)
+  (context-coloring-test-assert-region-level 25 34 1)
+  (context-coloring-test-assert-region-level 34 35 0)
+  (context-coloring-test-assert-region-level 35 52 1)
+  (context-coloring-test-assert-region-level 52 66 2)
+  (context-coloring-test-assert-region-level 66 72 1)
+  (context-coloring-test-assert-region-level 72 81 2)
+  (context-coloring-test-assert-region-level 81 82 1)
+  (context-coloring-test-assert-region-level 82 87 2)
+  (context-coloring-test-assert-region-level 87 89 1))
+
+(context-coloring-test-deftest-js-mode function-scopes)
+(context-coloring-test-deftest-js2-mode function-scopes)
+
+(defun context-coloring-test-js-global ()
+  (context-coloring-test-assert-region-level 20 28 1)
+  (context-coloring-test-assert-region-level 28 35 0)
+  (context-coloring-test-assert-region-level 35 41 1))
+
+(context-coloring-test-deftest-js-mode global)
+(context-coloring-test-deftest-js2-mode global)
+
+(defun context-coloring-test-js-block-scopes ()
+  (context-coloring-test-assert-region-level 20 64 1)
+   (setq context-coloring-js-block-scopes t)
+   (context-coloring-colorize)
+   (context-coloring-test-assert-region-level 20 27 1)
+   (context-coloring-test-assert-region-level 27 41 2)
+   (context-coloring-test-assert-region-level 41 42 1)
+   (context-coloring-test-assert-region-level 42 64 2))
+
+(context-coloring-test-deftest-js2-mode block-scopes)
+
+(defun context-coloring-test-js-catch ()
+  (context-coloring-test-assert-region-level 20 27 1)
+  (context-coloring-test-assert-region-level 27 51 2)
+  (context-coloring-test-assert-region-level 51 52 1)
+  (context-coloring-test-assert-region-level 52 73 2)
+  (context-coloring-test-assert-region-level 73 101 3)
+  (context-coloring-test-assert-region-level 101 102 1)
+  (context-coloring-test-assert-region-level 102 117 3)
+  (context-coloring-test-assert-region-level 117 123 2))
+
+(context-coloring-test-deftest-js-mode catch)
+(context-coloring-test-deftest-js2-mode catch)
+
+(defun context-coloring-test-js-key-names ()
+  (context-coloring-test-assert-region-level 20 63 1))
+
+(context-coloring-test-deftest-js-mode key-names)
+(context-coloring-test-deftest-js2-mode key-names)
+
+(defun context-coloring-test-js-property-lookup ()
+  (context-coloring-test-assert-region-level 20 26 0)
+  (context-coloring-test-assert-region-level 26 38 1)
+  (context-coloring-test-assert-region-level 38 44 0)
+  (context-coloring-test-assert-region-level 44 52 1)
+  (context-coloring-test-assert-region-level 57 63 0)
+  (context-coloring-test-assert-region-level 63 74 1))
+
+(context-coloring-test-deftest-js-mode property-lookup)
+(context-coloring-test-deftest-js2-mode property-lookup)
+
+(defun context-coloring-test-js-key-values ()
+  (context-coloring-test-assert-region-level 78 79 1))
+
+(context-coloring-test-deftest-js-mode key-values)
+(context-coloring-test-deftest-js2-mode key-values)
+
+(defun context-coloring-test-js-comments-and-strings ()
+  (context-coloring-test-assert-region-comment-delimiter 1 4)
+  (context-coloring-test-assert-region-comment 4 8)
+  (context-coloring-test-assert-region-comment-delimiter 9 12)
+  (context-coloring-test-assert-region-comment 12 19)
+  (context-coloring-test-assert-region-string 20 32)
+  (context-coloring-test-assert-region-level 32 33 0))
+
+(ert-deftest-async context-coloring-test-js-mode-comments-and-strings (done)
+  (context-coloring-test-js-mode
+   "./fixtures/comments-and-strings.js"
+   (lambda (teardown)
+     (unwind-protect
+         (context-coloring-test-js-comments-and-strings)
+       (funcall teardown))
+     (funcall done))
+   (lambda ()
+     (setq context-coloring-comments-and-strings t))))
+
+(ert-deftest context-coloring-test-js2-mode-comments-and-strings ()
+  (context-coloring-test-js2-mode
+   "./fixtures/comments-and-strings.js"
+   (setq context-coloring-comments-and-strings t)
+   (context-coloring-colorize)
+   (context-coloring-test-js-comments-and-strings)))
+
+(provide 'context-coloring-test)
+
+;;; context-coloring-test.el ends here
diff --git a/packages/context-coloring/test/fixtures/block-scopes.js b/packages/context-coloring/test/fixtures/block-scopes.js
new file mode 100644
index 0000000..735ca6f
--- /dev/null
+++ b/packages/context-coloring/test/fixtures/block-scopes.js
@@ -0,0 +1,6 @@
+(function () {
+    if (1) {
+        var a;
+        let b;
+    }
+}());
diff --git a/packages/context-coloring/test/fixtures/catch.js b/packages/context-coloring/test/fixtures/catch.js
new file mode 100644
index 0000000..a542ce9
--- /dev/null
+++ b/packages/context-coloring/test/fixtures/catch.js
@@ -0,0 +1,8 @@
+(function () {
+    try {} catch (e) {
+        var a = e;
+        try {} catch (e) {
+            var a = e;
+        }
+    }
+}());
diff --git a/packages/context-coloring/test/fixtures/comments-and-strings.js b/packages/context-coloring/test/fixtures/comments-and-strings.js
new file mode 100644
index 0000000..9910b02
--- /dev/null
+++ b/packages/context-coloring/test/fixtures/comments-and-strings.js
@@ -0,0 +1,3 @@
+// Foo.
+/* Bar. */
+'use strict';
diff --git a/packages/context-coloring/test/fixtures/function-scopes.js b/packages/context-coloring/test/fixtures/function-scopes.js
new file mode 100644
index 0000000..2f6ed32
--- /dev/null
+++ b/packages/context-coloring/test/fixtures/function-scopes.js
@@ -0,0 +1,5 @@
+var a = function () {};
+function b() {
+    var c = function () {};
+    function d() {}
+}
diff --git a/packages/context-coloring/test/fixtures/global.js b/packages/context-coloring/test/fixtures/global.js
new file mode 100644
index 0000000..a35619d
--- /dev/null
+++ b/packages/context-coloring/test/fixtures/global.js
@@ -0,0 +1,3 @@
+(function () {
+    var a = require('a');
+}());
diff --git a/packages/context-coloring/test/fixtures/key-names.js b/packages/context-coloring/test/fixtures/key-names.js
new file mode 100644
index 0000000..d8ad17c
--- /dev/null
+++ b/packages/context-coloring/test/fixtures/key-names.js
@@ -0,0 +1,6 @@
+(function () {
+    return {
+        a: 0,
+        b : 2
+    };
+}());
diff --git a/packages/context-coloring/test/fixtures/key-values.js b/packages/context-coloring/test/fixtures/key-values.js
new file mode 100644
index 0000000..41bdedd
--- /dev/null
+++ b/packages/context-coloring/test/fixtures/key-values.js
@@ -0,0 +1,8 @@
+(function () {
+    var a;
+    (function () {
+        return {
+            b: a
+        };
+    }());
+}());
diff --git a/packages/context-coloring/test/fixtures/property-lookup.js b/packages/context-coloring/test/fixtures/property-lookup.js
new file mode 100644
index 0000000..4edcb41
--- /dev/null
+++ b/packages/context-coloring/test/fixtures/property-lookup.js
@@ -0,0 +1,5 @@
+(function () {
+    window.foo();
+    window. bar();
+    window.foo.bar();
+}());
-- 
1.9.1


[-- Attachment #3: 0002-context-coloring-Add-metadata-and-exceptions.patch --]
[-- Type: text/x-patch, Size: 1550 bytes --]

From 3f8704183950661e2fd81f533bf226ec014b5dd5 Mon Sep 17 00:00:00 2001
From: Jackson Ray Hamilton <jackson@jacksonrayhamilton.com>
Date: Sun, 1 Feb 2015 23:07:15 -0800
Subject: [PATCH 2/2] context-coloring: Add metadata and exceptions

---
 copyright_exceptions | 1 +
 externals-list       | 1 +
 2 files changed, 2 insertions(+)

diff --git a/copyright_exceptions b/copyright_exceptions
index e6465d1..d1a40dc 100644
--- a/copyright_exceptions
+++ b/copyright_exceptions
@@ -59,6 +59,7 @@
 ./auctex/style/virtex.el
 ./auctex/tex-fold.el:    ("(C)" ("copyright"))
 ./auctex/tex-info.el:   '("copyright" nil)
+./context-coloring/libraries/ert-async.el:;; Copyright (C) 2014 Johan Andersson
 ./ergoemacs-mode/ergoemacs-advices.el:                         emacs-copyright
 ./ergoemacs-mode/ergoemacs-themes.el:                 ,(lambda () emacs-copyright)
 ./gnugo/gnugo.el:    (CP "Copyright"       game  simpletext)
diff --git a/externals-list b/externals-list
index 19b5af1..ce79467 100644
--- a/externals-list
+++ b/externals-list
@@ -23,6 +23,7 @@
  ("chess"		:external nil) ;; Was https://github.com/jwiegley/emacs-chess.git
  ("coffee-mode"		:subtree "https://github.com/defunkt/coffee-mode")
  ("company"		:subtree "https://github.com/company-mode/company-mode.git")
+ ("context-coloring"	:subtree "https://github.com/jacksonrayhamilton/context-coloring.git")
  ("darkroom"            :subtree "https://github.com/capitaomorte/darkroom.git")
  ("diff-hl"		:subtree "https://github.com/dgutov/diff-hl.git")
  ("dismal"		:external nil)
-- 
1.9.1


[-- Attachment #4: 0001-hydra-test.el-Add-copyright-notice.patch --]
[-- Type: text/x-patch, Size: 1437 bytes --]

From ae9b54c9dd7b74f7a2d475131e2ea65144e42335 Mon Sep 17 00:00:00 2001
From: Jackson Ray Hamilton <jackson@jacksonrayhamilton.com>
Date: Sun, 1 Feb 2015 23:06:15 -0800
Subject: [PATCH 1/2] hydra-test.el: Add copyright notice

---
 packages/hydra/hydra-test.el | 27 +++++++++++++++++++++++++++
 1 file changed, 27 insertions(+)

diff --git a/packages/hydra/hydra-test.el b/packages/hydra/hydra-test.el
index d1cb902..e5f8e7d 100644
--- a/packages/hydra/hydra-test.el
+++ b/packages/hydra/hydra-test.el
@@ -1,3 +1,30 @@
+;;; hydra-test.el --- Tests for Hydra
+
+;; Copyright (C) 2015  Free Software Foundation, Inc.
+
+;; Author: Oleh Krehel
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; Tests for Hydra.
+
+;;; Code:
+
 (require 'ert)
 
 (ert-deftest defhydra ()
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* Re: Requesting review before pushing patch to ELPA
  2015-02-02  7:49 Requesting review before pushing patch to ELPA Jackson Hamilton
@ 2015-02-02 17:58 ` Stefan Monnier
  2015-02-03  5:57   ` Jackson Hamilton
  2015-02-04 15:13 ` Stefan Monnier
  1 sibling, 1 reply; 20+ messages in thread
From: Stefan Monnier @ 2015-02-02 17:58 UTC (permalink / raw)
  To: Jackson Hamilton; +Cc: emacs-devel

> which included 247 commits. I am not sure if these should be squashed, but
> based on the commit log it seems like this subtree approach is how other
> people are managing their externally-maintained packages, so I assume it is
> okay to add the whole history.

Indeed, it's OK to keep the history.

> On top of that, I added 2 patches so that I could test ELPA locally. I was
> able to install my package via a "local-elpa" as described in the README.
> The patches for that are attached in this email.

I think the hydra-test.el has been fixed by someone else in the
mean time.  As for your change:

> +./context-coloring/libraries/ert-async.el:;; Copyright (C) 2014 Johan Andersson

I think this change is incorrect.  IIUC this is johan.rejeep@gmail.com
we're talking bout, and he signed the copyright assignment forms, so
the line in ert-async.el should say "Copyright (C) 2014 Free Software
Foundation, Inc" (at which point you won't need any change to copyright_exceptions).


        Stefan



^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: Requesting review before pushing patch to ELPA
  2015-02-02 17:58 ` Stefan Monnier
@ 2015-02-03  5:57   ` Jackson Hamilton
  2015-02-03 12:13     ` Artur Malabarba
  2015-02-03 17:51     ` Stefan Monnier
  0 siblings, 2 replies; 20+ messages in thread
From: Jackson Hamilton @ 2015-02-03  5:57 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: emacs-devel

[-- Attachment #1: Type: text/plain, Size: 1585 bytes --]

On the "Copyright (C) 2014 Johan Andersson", are you suggesting I change
the copyright notice in ert-async.el myself, or that I should contact the
author and tell him to change it?

In either case that seems inappropriate.

context-coloring/languages/javascript/libraries/ also includes 3 JavaScript
libraries with their own copyright notices. These appear to be licensed
under the FreeBSD license. Should they be handled specially?

Regards,
Jackson

On Mon, Feb 2, 2015 at 9:58 AM, Stefan Monnier <monnier@iro.umontreal.ca>
wrote:

> > which included 247 commits. I am not sure if these should be squashed,
> but
> > based on the commit log it seems like this subtree approach is how other
> > people are managing their externally-maintained packages, so I assume it
> is
> > okay to add the whole history.
>
> Indeed, it's OK to keep the history.
>
> > On top of that, I added 2 patches so that I could test ELPA locally. I
> was
> > able to install my package via a "local-elpa" as described in the README.
> > The patches for that are attached in this email.
>
> I think the hydra-test.el has been fixed by someone else in the
> mean time.  As for your change:
>
> > +./context-coloring/libraries/ert-async.el:;; Copyright (C) 2014 Johan
> Andersson
>
> I think this change is incorrect.  IIUC this is johan.rejeep@gmail.com
> we're talking bout, and he signed the copyright assignment forms, so
> the line in ert-async.el should say "Copyright (C) 2014 Free Software
> Foundation, Inc" (at which point you won't need any change to
> copyright_exceptions).
>
>
>         Stefan
>

[-- Attachment #2: Type: text/html, Size: 2389 bytes --]

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: Requesting review before pushing patch to ELPA
  2015-02-03  5:57   ` Jackson Hamilton
@ 2015-02-03 12:13     ` Artur Malabarba
  2015-02-04  9:24       ` Jackson Hamilton
  2015-02-03 17:51     ` Stefan Monnier
  1 sibling, 1 reply; 20+ messages in thread
From: Artur Malabarba @ 2015-02-03 12:13 UTC (permalink / raw)
  To: Jackson Hamilton; +Cc: Stefan Monnier, emacs-devel

If that package is only used for testing, you can also just not merge
it into elpa and do your testing elsewhere (e.g. Travis+github).
That's what I did with Names, because the tests involved a lot of
packages from other people with a ton of changes made by me. I'm not
saying I couldn't put those inside the Elpa/package/ directory, but it
was easier this way than to worry about copyrights.

I can go into the `git' details necessary for this if you'd like.

2015-02-03 5:57 GMT+00:00 Jackson Hamilton <jackson@jacksonrayhamilton.com>:
> On the "Copyright (C) 2014 Johan Andersson", are you suggesting I change the
> copyright notice in ert-async.el myself, or that I should contact the author
> and tell him to change it?
>
> In either case that seems inappropriate.
>
> context-coloring/languages/javascript/libraries/ also includes 3 JavaScript
> libraries with their own copyright notices. These appear to be licensed
> under the FreeBSD license. Should they be handled specially?
>
> Regards,
> Jackson
>
> On Mon, Feb 2, 2015 at 9:58 AM, Stefan Monnier <monnier@iro.umontreal.ca>
> wrote:
>>
>> > which included 247 commits. I am not sure if these should be squashed,
>> > but
>> > based on the commit log it seems like this subtree approach is how other
>> > people are managing their externally-maintained packages, so I assume it
>> > is
>> > okay to add the whole history.
>>
>> Indeed, it's OK to keep the history.
>>
>> > On top of that, I added 2 patches so that I could test ELPA locally. I
>> > was
>> > able to install my package via a "local-elpa" as described in the
>> > README.
>> > The patches for that are attached in this email.
>>
>> I think the hydra-test.el has been fixed by someone else in the
>> mean time.  As for your change:
>>
>> > +./context-coloring/libraries/ert-async.el:;; Copyright (C) 2014 Johan
>> > Andersson
>>
>> I think this change is incorrect.  IIUC this is johan.rejeep@gmail.com
>> we're talking bout, and he signed the copyright assignment forms, so
>> the line in ert-async.el should say "Copyright (C) 2014 Free Software
>> Foundation, Inc" (at which point you won't need any change to
>> copyright_exceptions).
>>
>>
>>         Stefan
>
>



^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: Requesting review before pushing patch to ELPA
  2015-02-03  5:57   ` Jackson Hamilton
  2015-02-03 12:13     ` Artur Malabarba
@ 2015-02-03 17:51     ` Stefan Monnier
  2015-02-04  9:32       ` Jackson Hamilton
  1 sibling, 1 reply; 20+ messages in thread
From: Stefan Monnier @ 2015-02-03 17:51 UTC (permalink / raw)
  To: Jackson Hamilton; +Cc: emacs-devel

> On the "Copyright (C) 2014 Johan Andersson", are you suggesting I change
> the copyright notice in ert-async.el myself, or that I should contact the
> author and tell him to change it?

Asking him is the right way.  The question to ask is whether he wants to
contribute this code to Emacs and/or GNU ELPA: if he does, then that
means his copyright assignment does cover this file and so that means
that the "Copyright (C) 2014 Johan Andersson" should be changed to
"Copyright (C) 2014 Free Software Foundation, Inc".

Of course, you want to keep his name as an "Author:" and/or "Maintainer:".

> context-coloring/languages/javascript/libraries/ also includes 3 JavaScript
> libraries with their own copyright notices.  These appear to be licensed
> under the FreeBSD license.  Should they be handled specially?

The problem is not just the license but the copyright.  Who owns the
copyright to those files?


        Stefan



^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: Requesting review before pushing patch to ELPA
  2015-02-03 12:13     ` Artur Malabarba
@ 2015-02-04  9:24       ` Jackson Hamilton
  2015-02-04 11:15         ` Artur Malabarba
  0 siblings, 1 reply; 20+ messages in thread
From: Jackson Hamilton @ 2015-02-04  9:24 UTC (permalink / raw)
  To: bruce.connor.am; +Cc: emacs-devel

[-- Attachment #1: Type: text/plain, Size: 2475 bytes --]

Hi Artur, I am interested in the "git details". Please elaborate.

On Tue, Feb 3, 2015 at 4:13 AM, Artur Malabarba <bruce.connor.am@gmail.com>
wrote:

> If that package is only used for testing, you can also just not merge
> it into elpa and do your testing elsewhere (e.g. Travis+github).
> That's what I did with Names, because the tests involved a lot of
> packages from other people with a ton of changes made by me. I'm not
> saying I couldn't put those inside the Elpa/package/ directory, but it
> was easier this way than to worry about copyrights.
>
> I can go into the `git' details necessary for this if you'd like.
>
> 2015-02-03 5:57 GMT+00:00 Jackson Hamilton <jackson@jacksonrayhamilton.com
> >:
> > On the "Copyright (C) 2014 Johan Andersson", are you suggesting I change
> the
> > copyright notice in ert-async.el myself, or that I should contact the
> author
> > and tell him to change it?
> >
> > In either case that seems inappropriate.
> >
> > context-coloring/languages/javascript/libraries/ also includes 3
> JavaScript
> > libraries with their own copyright notices. These appear to be licensed
> > under the FreeBSD license. Should they be handled specially?
> >
> > Regards,
> > Jackson
> >
> > On Mon, Feb 2, 2015 at 9:58 AM, Stefan Monnier <monnier@iro.umontreal.ca
> >
> > wrote:
> >>
> >> > which included 247 commits. I am not sure if these should be squashed,
> >> > but
> >> > based on the commit log it seems like this subtree approach is how
> other
> >> > people are managing their externally-maintained packages, so I assume
> it
> >> > is
> >> > okay to add the whole history.
> >>
> >> Indeed, it's OK to keep the history.
> >>
> >> > On top of that, I added 2 patches so that I could test ELPA locally. I
> >> > was
> >> > able to install my package via a "local-elpa" as described in the
> >> > README.
> >> > The patches for that are attached in this email.
> >>
> >> I think the hydra-test.el has been fixed by someone else in the
> >> mean time.  As for your change:
> >>
> >> > +./context-coloring/libraries/ert-async.el:;; Copyright (C) 2014 Johan
> >> > Andersson
> >>
> >> I think this change is incorrect.  IIUC this is johan.rejeep@gmail.com
> >> we're talking bout, and he signed the copyright assignment forms, so
> >> the line in ert-async.el should say "Copyright (C) 2014 Free Software
> >> Foundation, Inc" (at which point you won't need any change to
> >> copyright_exceptions).
> >>
> >>
> >>         Stefan
> >
> >
>

[-- Attachment #2: Type: text/html, Size: 3478 bytes --]

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: Requesting review before pushing patch to ELPA
  2015-02-03 17:51     ` Stefan Monnier
@ 2015-02-04  9:32       ` Jackson Hamilton
  2015-02-04 15:14         ` Stefan Monnier
  0 siblings, 1 reply; 20+ messages in thread
From: Jackson Hamilton @ 2015-02-04  9:32 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: emacs-devel

[-- Attachment #1: Type: text/plain, Size: 1732 bytes --]

There are a whole bunch of people who claim copyright on those files; their
names are listed in the files' headers. I'll note that when I requested
copyright assignment, I alerted the clerk that I was using these libraries.
The plugin doesn't work without them.

I could probably externalize those files into an "npm" package and instruct
users to install a separate executable to run the JavaScript portion of the
plugin. But I'd much rather just include them in the package, because then
it "works out-of-the-box".

As for ert-async, what would be our course of action if he does not agree?
Will we add a copyright exception, or will I not be able to include the
file in the repo?

On Tue, Feb 3, 2015 at 9:51 AM, Stefan Monnier <monnier@iro.umontreal.ca>
wrote:

> > On the "Copyright (C) 2014 Johan Andersson", are you suggesting I change
> > the copyright notice in ert-async.el myself, or that I should contact the
> > author and tell him to change it?
>
> Asking him is the right way.  The question to ask is whether he wants to
> contribute this code to Emacs and/or GNU ELPA: if he does, then that
> means his copyright assignment does cover this file and so that means
> that the "Copyright (C) 2014 Johan Andersson" should be changed to
> "Copyright (C) 2014 Free Software Foundation, Inc".
>
> Of course, you want to keep his name as an "Author:" and/or "Maintainer:".
>
> > context-coloring/languages/javascript/libraries/ also includes 3
> JavaScript
> > libraries with their own copyright notices.  These appear to be licensed
> > under the FreeBSD license.  Should they be handled specially?
>
> The problem is not just the license but the copyright.  Who owns the
> copyright to those files?
>
>
>         Stefan
>

[-- Attachment #2: Type: text/html, Size: 2339 bytes --]

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: Requesting review before pushing patch to ELPA
  2015-02-04  9:24       ` Jackson Hamilton
@ 2015-02-04 11:15         ` Artur Malabarba
  2015-02-04 11:16           ` Artur Malabarba
  0 siblings, 1 reply; 20+ messages in thread
From: Artur Malabarba @ 2015-02-04 11:15 UTC (permalink / raw)
  To: Jackson Hamilton; +Cc: emacs-devel

You have two options. In both cases, you'll have a separate elpa
branch on your repo. Whenever you do your git subtree merging into the
GNU Elpa repository, use your repo's elpa branch as the remote
(instead of master).

You can:

Create an elpa branch in your repo, child of the master branch.
Delete from the elpa branch everything but the package source files.
Whenever you update master, merge that into elpa.

This has two small disadvantages. - Every time you edit a test file
you'll get a merge conflict when you try to merge master into elpa. -
Even though you deleted the files from HEAD, they will still exist in
the history of your repo (and therefore, in the history of the GNU
Elpa repo).

You can also (this is the one I used):

Create an orphan elpa branch, and manually place in there only the
files you're ok with.
Delete and recreate the master branch.
Merge elpa into master, then manually add to master all of the files
you didn't want on elpa.
Do your development on the elpa branch, and merge that into master.

This has the advantage that the unwanted files will be wiped from
history, so the GNU Elpa repo will never know about them. It has the
obvious disadvantage that it will completely reset your repo's
history.


Cheers

2015-02-04 7:24 GMT-02:00 Jackson Hamilton <jackson@jacksonrayhamilton.com>:
> Hi Artur, I am interested in the "git details". Please elaborate.
>
> On Tue, Feb 3, 2015 at 4:13 AM, Artur Malabarba <bruce.connor.am@gmail.com>
> wrote:
>>
>> If that package is only used for testing, you can also just not merge
>> it into elpa and do your testing elsewhere (e.g. Travis+github).
>> That's what I did with Names, because the tests involved a lot of
>> packages from other people with a ton of changes made by me. I'm not
>> saying I couldn't put those inside the Elpa/package/ directory, but it
>> was easier this way than to worry about copyrights.
>>
>> I can go into the `git' details necessary for this if you'd like.
>>
>> 2015-02-03 5:57 GMT+00:00 Jackson Hamilton
>> <jackson@jacksonrayhamilton.com>:
>> > On the "Copyright (C) 2014 Johan Andersson", are you suggesting I change
>> > the
>> > copyright notice in ert-async.el myself, or that I should contact the
>> > author
>> > and tell him to change it?
>> >
>> > In either case that seems inappropriate.
>> >
>> > context-coloring/languages/javascript/libraries/ also includes 3
>> > JavaScript
>> > libraries with their own copyright notices. These appear to be licensed
>> > under the FreeBSD license. Should they be handled specially?
>> >
>> > Regards,
>> > Jackson
>> >
>> > On Mon, Feb 2, 2015 at 9:58 AM, Stefan Monnier
>> > <monnier@iro.umontreal.ca>
>> > wrote:
>> >>
>> >> > which included 247 commits. I am not sure if these should be
>> >> > squashed,
>> >> > but
>> >> > based on the commit log it seems like this subtree approach is how
>> >> > other
>> >> > people are managing their externally-maintained packages, so I assume
>> >> > it
>> >> > is
>> >> > okay to add the whole history.
>> >>
>> >> Indeed, it's OK to keep the history.
>> >>
>> >> > On top of that, I added 2 patches so that I could test ELPA locally.
>> >> > I
>> >> > was
>> >> > able to install my package via a "local-elpa" as described in the
>> >> > README.
>> >> > The patches for that are attached in this email.
>> >>
>> >> I think the hydra-test.el has been fixed by someone else in the
>> >> mean time.  As for your change:
>> >>
>> >> > +./context-coloring/libraries/ert-async.el:;; Copyright (C) 2014
>> >> > Johan
>> >> > Andersson
>> >>
>> >> I think this change is incorrect.  IIUC this is johan.rejeep@gmail.com
>> >> we're talking bout, and he signed the copyright assignment forms, so
>> >> the line in ert-async.el should say "Copyright (C) 2014 Free Software
>> >> Foundation, Inc" (at which point you won't need any change to
>> >> copyright_exceptions).
>> >>
>> >>
>> >>         Stefan
>> >
>> >
>
>



^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: Requesting review before pushing patch to ELPA
  2015-02-04 11:15         ` Artur Malabarba
@ 2015-02-04 11:16           ` Artur Malabarba
  0 siblings, 0 replies; 20+ messages in thread
From: Artur Malabarba @ 2015-02-04 11:16 UTC (permalink / raw)
  To: Jackson Hamilton; +Cc: emacs-devel

[-- Attachment #1: Type: text/plain, Size: 5610 bytes --]

The formatting of that e-mail was all off. Let me try again with rich text:
------------------------------

You have two options. In both cases, you'll have a separate elpa branch on
your repo. Whenever you do your git subtree merging into the GNU Elpa
repository, use your repo's elpa branch as the remote (instead of master).

You can:

   1. Create an elpa branch in your repo, child of the master branch.
   2. Delete from the elpa branch everything but the package source files.
   3. Whenever you update master, merge that into elpa.

This has two small disadvantages.

   - Every time you edit a test file you'll get a merge conflict when you
   try to merge master into elpa.
   - Even though you deleted the files from HEAD, they will still exist in
   the history of your repo (and therefore, in the history of the GNU Elpa
   repo).

You can also:

   1. Create an *orphan* elpa branch, and manually place in there *only*
   the files you're ok with.
   2. Delete and recreate the master branch.
   3. Merge elpa into master, then manually add to master all of the files
   you didn't want on elpa.
   4. Do your development on the elpa branch, and merge that into master.

This has the advantage that the unwanted files will be wiped from history,
so the GNU Elpa repo will never know about them. It has the obvious
disadvantage that it will completely reset your repo's history.

2015-02-04 9:15 GMT-02:00 Artur Malabarba <bruce.connor.am@gmail.com>:

> You have two options. In both cases, you'll have a separate elpa
> branch on your repo. Whenever you do your git subtree merging into the
> GNU Elpa repository, use your repo's elpa branch as the remote
> (instead of master).
>
> You can:
>
> Create an elpa branch in your repo, child of the master branch.
> Delete from the elpa branch everything but the package source files.
> Whenever you update master, merge that into elpa.
>
> This has two small disadvantages. - Every time you edit a test file
> you'll get a merge conflict when you try to merge master into elpa. -
> Even though you deleted the files from HEAD, they will still exist in
> the history of your repo (and therefore, in the history of the GNU
> Elpa repo).
>
> You can also (this is the one I used):
>
> Create an orphan elpa branch, and manually place in there only the
> files you're ok with.
> Delete and recreate the master branch.
> Merge elpa into master, then manually add to master all of the files
> you didn't want on elpa.
> Do your development on the elpa branch, and merge that into master.
>
> This has the advantage that the unwanted files will be wiped from
> history, so the GNU Elpa repo will never know about them. It has the
> obvious disadvantage that it will completely reset your repo's
> history.
>
>
> Cheers
>
> 2015-02-04 7:24 GMT-02:00 Jackson Hamilton <jackson@jacksonrayhamilton.com
> >:
> > Hi Artur, I am interested in the "git details". Please elaborate.
> >
> > On Tue, Feb 3, 2015 at 4:13 AM, Artur Malabarba <
> bruce.connor.am@gmail.com>
> > wrote:
> >>
> >> If that package is only used for testing, you can also just not merge
> >> it into elpa and do your testing elsewhere (e.g. Travis+github).
> >> That's what I did with Names, because the tests involved a lot of
> >> packages from other people with a ton of changes made by me. I'm not
> >> saying I couldn't put those inside the Elpa/package/ directory, but it
> >> was easier this way than to worry about copyrights.
> >>
> >> I can go into the `git' details necessary for this if you'd like.
> >>
> >> 2015-02-03 5:57 GMT+00:00 Jackson Hamilton
> >> <jackson@jacksonrayhamilton.com>:
> >> > On the "Copyright (C) 2014 Johan Andersson", are you suggesting I
> change
> >> > the
> >> > copyright notice in ert-async.el myself, or that I should contact the
> >> > author
> >> > and tell him to change it?
> >> >
> >> > In either case that seems inappropriate.
> >> >
> >> > context-coloring/languages/javascript/libraries/ also includes 3
> >> > JavaScript
> >> > libraries with their own copyright notices. These appear to be
> licensed
> >> > under the FreeBSD license. Should they be handled specially?
> >> >
> >> > Regards,
> >> > Jackson
> >> >
> >> > On Mon, Feb 2, 2015 at 9:58 AM, Stefan Monnier
> >> > <monnier@iro.umontreal.ca>
> >> > wrote:
> >> >>
> >> >> > which included 247 commits. I am not sure if these should be
> >> >> > squashed,
> >> >> > but
> >> >> > based on the commit log it seems like this subtree approach is how
> >> >> > other
> >> >> > people are managing their externally-maintained packages, so I
> assume
> >> >> > it
> >> >> > is
> >> >> > okay to add the whole history.
> >> >>
> >> >> Indeed, it's OK to keep the history.
> >> >>
> >> >> > On top of that, I added 2 patches so that I could test ELPA
> locally.
> >> >> > I
> >> >> > was
> >> >> > able to install my package via a "local-elpa" as described in the
> >> >> > README.
> >> >> > The patches for that are attached in this email.
> >> >>
> >> >> I think the hydra-test.el has been fixed by someone else in the
> >> >> mean time.  As for your change:
> >> >>
> >> >> > +./context-coloring/libraries/ert-async.el:;; Copyright (C) 2014
> >> >> > Johan
> >> >> > Andersson
> >> >>
> >> >> I think this change is incorrect.  IIUC this is
> johan.rejeep@gmail.com
> >> >> we're talking bout, and he signed the copyright assignment forms, so
> >> >> the line in ert-async.el should say "Copyright (C) 2014 Free Software
> >> >> Foundation, Inc" (at which point you won't need any change to
> >> >> copyright_exceptions).
> >> >>
> >> >>
> >> >>         Stefan
> >> >
> >> >
> >
> >
>

[-- Attachment #2: Type: text/html, Size: 7807 bytes --]

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: Requesting review before pushing patch to ELPA
  2015-02-02  7:49 Requesting review before pushing patch to ELPA Jackson Hamilton
  2015-02-02 17:58 ` Stefan Monnier
@ 2015-02-04 15:13 ` Stefan Monnier
  1 sibling, 0 replies; 20+ messages in thread
From: Stefan Monnier @ 2015-02-04 15:13 UTC (permalink / raw)
  To: Jackson Hamilton; +Cc: emacs-devel

>  packages/context-coloring/libraries/ert-async.el   |    89 +

BTW, this should probably just be included in ert itself (tho you'll
probably want to keep a copy when used on older Emacsen).

>  packages/context-coloring/libraries/js2-mode.el    | 12331

And this should not be included in context-coloring at all: just add it
as a requirement via something like:

;; Package-Requires: ((js2-mode))


        Stefan



^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: Requesting review before pushing patch to ELPA
  2015-02-04  9:32       ` Jackson Hamilton
@ 2015-02-04 15:14         ` Stefan Monnier
  2015-02-04 19:46           ` Jackson Hamilton
  0 siblings, 1 reply; 20+ messages in thread
From: Stefan Monnier @ 2015-02-04 15:14 UTC (permalink / raw)
  To: Jackson Hamilton; +Cc: emacs-devel

> There are a whole bunch of people who claim copyright on those files; their
> names are listed in the files' headers. I'll note that when I requested
> copyright assignment, I alerted the clerk that I was using these libraries.
> The plugin doesn't work without them.

Does context-coloring not work at all without those files, or does it
work just fine for some modes but not for others (presumably not for
javascript mode)?

> I could probably externalize those files into an "npm" package and instruct
> users to install a separate executable to run the JavaScript portion of the
> plugin.  But I'd much rather just include them in the package, because then
> it "works out-of-the-box".

Can you give me some background about where those files come from?
Have they been written specifically for context-coloring or are they
taken from some other project?

> As for ert-async, what would be our course of action if he does not agree?

While it's of course possible, it seems highly unlikely, so I wouldn't
worry about it until that happens.


        Stefan



^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: Requesting review before pushing patch to ELPA
  2015-02-04 15:14         ` Stefan Monnier
@ 2015-02-04 19:46           ` Jackson Hamilton
  2015-02-04 19:48             ` Dmitry Gutov
  2015-02-04 21:58             ` Stefan Monnier
  0 siblings, 2 replies; 20+ messages in thread
From: Jackson Hamilton @ 2015-02-04 19:46 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: emacs-devel

[-- Attachment #1: Type: text/plain, Size: 2096 bytes --]

js2-mode is included in the repo for development and CI purposes. I want to
test in a clean environment (emacs -Q) and also on Travis CI. Having that
file available lets me easily add it to the load path. I do exclude it in
.elpaignore, because I expect that when context-coloring is installed, the
package manager will handle the dependency instead.

Yes, it is a truer statement that (in its current state) context-coloring
works fine for some modes, but requires those files for non-js2-mode
JavaScript modes.

esprima.js is sourced from here:
https://github.com/jquery/esprima/blob/1114c32c4e0ffaf47864967a835fdc0a37909f14/esprima.js
escope.js is sourced from here:
https://github.com/estools/escope/blob/b191ec36b744f65df094c614510ca6125284a958/escope.js
extraverse.js is sourced from here:
https://github.com/estools/estraverse/blob/c280df4aa75d7846328aa436ee5acd04afafc543/estraverse.js

On Wed, Feb 4, 2015 at 7:14 AM, Stefan Monnier <monnier@iro.umontreal.ca>
wrote:

> > There are a whole bunch of people who claim copyright on those files;
> their
> > names are listed in the files' headers. I'll note that when I requested
> > copyright assignment, I alerted the clerk that I was using these
> libraries.
> > The plugin doesn't work without them.
>
> Does context-coloring not work at all without those files, or does it
> work just fine for some modes but not for others (presumably not for
> javascript mode)?
>
> > I could probably externalize those files into an "npm" package and
> instruct
> > users to install a separate executable to run the JavaScript portion of
> the
> > plugin.  But I'd much rather just include them in the package, because
> then
> > it "works out-of-the-box".
>
> Can you give me some background about where those files come from?
> Have they been written specifically for context-coloring or are they
> taken from some other project?
>
> > As for ert-async, what would be our course of action if he does not
> agree?
>
> While it's of course possible, it seems highly unlikely, so I wouldn't
> worry about it until that happens.
>
>
>         Stefan
>

[-- Attachment #2: Type: text/html, Size: 3046 bytes --]

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: Requesting review before pushing patch to ELPA
  2015-02-04 19:46           ` Jackson Hamilton
@ 2015-02-04 19:48             ` Dmitry Gutov
  2015-02-04 21:58             ` Stefan Monnier
  1 sibling, 0 replies; 20+ messages in thread
From: Dmitry Gutov @ 2015-02-04 19:48 UTC (permalink / raw)
  To: Jackson Hamilton, Stefan Monnier; +Cc: emacs-devel

On 02/04/2015 09:46 PM, Jackson Hamilton wrote:
> js2-mode is included in the repo for development and CI purposes. I want
> to test in a clean environment (emacs -Q) and also on Travis CI.

Why not ask Travis to install it?



^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: Requesting review before pushing patch to ELPA
  2015-02-04 19:46           ` Jackson Hamilton
  2015-02-04 19:48             ` Dmitry Gutov
@ 2015-02-04 21:58             ` Stefan Monnier
  2015-02-05  8:05               ` Jackson Hamilton
  1 sibling, 1 reply; 20+ messages in thread
From: Stefan Monnier @ 2015-02-04 21:58 UTC (permalink / raw)
  To: Jackson Hamilton; +Cc: emacs-devel

> js2-mode is included in the repo for development and CI purposes.  I want to
> test in a clean environment (emacs -Q) and also on Travis CI.  Having that
> file available lets me easily add it to the load path.  I do exclude it in
> .elpaignore, because I expect that when context-coloring is installed, the
> package manager will handle the dependency instead.

Ah, right, that makes sense.

> Yes, it is a truer statement that (in its current state) context-coloring
> works fine for some modes, but requires those files for non-js2-mode
> JavaScript modes.

> esprima.js is sourced from here:
> https://github.com/jquery/esprima/blob/1114c32c4e0ffaf47864967a835fdc0a37909f14/esprima.js
> escope.js is sourced from here:
> https://github.com/estools/escope/blob/b191ec36b744f65df094c614510ca6125284a958/escope.js
> extraverse.js is sourced from here:
> https://github.com/estools/estraverse/blob/c280df4aa75d7846328aa436ee5acd04afafc543/estraverse.js

As long as we only redistribute these files without modifying them at
all, I think it's fine to have them as-is in elpa.git.

If I were you, I'd probably prefer to split them into a separate
"javascript support for context-coloring" package, but luckily I'm not
you, so feel free to do as you wish in this regard.


        Stefan



^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: Requesting review before pushing patch to ELPA
  2015-02-04 21:58             ` Stefan Monnier
@ 2015-02-05  8:05               ` Jackson Hamilton
  2015-02-05 14:10                 ` Stefan Monnier
  0 siblings, 1 reply; 20+ messages in thread
From: Jackson Hamilton @ 2015-02-05  8:05 UTC (permalink / raw)
  To: emacs-devel


[-- Attachment #1.1: Type: text/plain, Size: 1849 bytes --]

Okay. Round two on merging context-coloring:

I've externalized the non-js2-mode JavaScript code, which means the
esprima, escope and extraverse files are no longer present.

I've also added a script to download the latest versions of ert-async and
js2-mode, so those aren't present anymore either.

The attached patches are the current representation of what I intend to
merge. (Same subtree strategy as before.)

On Wed, Feb 4, 2015 at 1:58 PM, Stefan Monnier <monnier@iro.umontreal.ca>
wrote:

> > js2-mode is included in the repo for development and CI purposes.  I
> want to
> > test in a clean environment (emacs -Q) and also on Travis CI.  Having
> that
> > file available lets me easily add it to the load path.  I do exclude it
> in
> > .elpaignore, because I expect that when context-coloring is installed,
> the
> > package manager will handle the dependency instead.
>
> Ah, right, that makes sense.
>
> > Yes, it is a truer statement that (in its current state) context-coloring
> > works fine for some modes, but requires those files for non-js2-mode
> > JavaScript modes.
>
> > esprima.js is sourced from here:
> >
> https://github.com/jquery/esprima/blob/1114c32c4e0ffaf47864967a835fdc0a37909f14/esprima.js
> > escope.js is sourced from here:
> >
> https://github.com/estools/escope/blob/b191ec36b744f65df094c614510ca6125284a958/escope.js
> > extraverse.js is sourced from here:
> >
> https://github.com/estools/estraverse/blob/c280df4aa75d7846328aa436ee5acd04afafc543/estraverse.js
>
> As long as we only redistribute these files without modifying them at
> all, I think it's fine to have them as-is in elpa.git.
>
> If I were you, I'd probably prefer to split them into a separate
> "javascript support for context-coloring" package, but luckily I'm not
> you, so feel free to do as you wish in this regard.
>
>
>         Stefan
>

[-- Attachment #1.2: Type: text/html, Size: 2841 bytes --]

[-- Attachment #2: 0001-externals-list-Add-context-coloring.patch --]
[-- Type: text/x-patch, Size: 917 bytes --]

From 784910cc972e5cbf97bdd2ef66dde880b0cfaf7f Mon Sep 17 00:00:00 2001
From: Jackson Ray Hamilton <jackson@jacksonrayhamilton.com>
Date: Wed, 4 Feb 2015 23:40:36 -0800
Subject: [PATCH] externals-list: Add context-coloring

---
 externals-list | 1 +
 1 file changed, 1 insertion(+)

diff --git a/externals-list b/externals-list
index 19b5af1..ce79467 100644
--- a/externals-list
+++ b/externals-list
@@ -23,6 +23,7 @@
  ("chess"		:external nil) ;; Was https://github.com/jwiegley/emacs-chess.git
  ("coffee-mode"		:subtree "https://github.com/defunkt/coffee-mode")
  ("company"		:subtree "https://github.com/company-mode/company-mode.git")
+ ("context-coloring"	:subtree "https://github.com/jacksonrayhamilton/context-coloring.git")
  ("darkroom"            :subtree "https://github.com/capitaomorte/darkroom.git")
  ("diff-hl"		:subtree "https://github.com/dgutov/diff-hl.git")
  ("dismal"		:external nil)
-- 
1.9.1


[-- Attachment #3: 0001-squashed-merge.patch --]
[-- Type: text/x-patch, Size: 623498 bytes --]

From 39800400f257756b82b7f42f0b459e9f416707e0 Mon Sep 17 00:00:00 2001
From: Jackson Ray Hamilton <jackson@jacksonrayhamilton.com>
Date: Wed, 4 Feb 2015 23:48:29 -0800
Subject: [PATCH] squashed merge

---
 packages/context-coloring/.gitignore               |    3 +
 packages/context-coloring/.travis.yml              |   21 +
 packages/context-coloring/Makefile                 |   37 +
 packages/context-coloring/README.md                |  165 +
 .../benchmark/context-coloring-benchmark.el        |  160 +
 .../benchmark/fixtures/.dir-locals.el              |    1 +
 .../benchmark/fixtures/async-0.9.0.js              | 1123 +++
 .../benchmark/fixtures/jquery-2.1.1.js             | 9190 ++++++++++++++++++++
 .../benchmark/fixtures/lodash-2.4.1.js             | 6785 +++++++++++++++
 .../benchmark/fixtures/mkdirp-0.5.0.js             |   97 +
 packages/context-coloring/context-coloring.el      |  607 ++
 packages/context-coloring/scopifier.png            |  Bin 0 -> 2609 bytes
 packages/context-coloring/screenshot.png           |  Bin 0 -> 22006 bytes
 .../scripts/download-dependencies.el               |   40 +
 .../context-coloring/test/context-coloring-test.el |  375 +
 .../context-coloring/test/fixtures/block-scopes.js |    6 +
 packages/context-coloring/test/fixtures/catch.js   |    8 +
 .../test/fixtures/comments-and-strings.js          |    3 +
 .../test/fixtures/function-scopes.js               |    5 +
 packages/context-coloring/test/fixtures/global.js  |    3 +
 .../context-coloring/test/fixtures/key-names.js    |    6 +
 .../context-coloring/test/fixtures/key-values.js   |    8 +
 .../test/fixtures/property-lookup.js               |    5 +
 23 files changed, 18648 insertions(+)
 create mode 100644 packages/context-coloring/.gitignore
 create mode 100644 packages/context-coloring/.travis.yml
 create mode 100644 packages/context-coloring/Makefile
 create mode 100644 packages/context-coloring/README.md
 create mode 100644 packages/context-coloring/benchmark/context-coloring-benchmark.el
 create mode 100644 packages/context-coloring/benchmark/fixtures/.dir-locals.el
 create mode 100644 packages/context-coloring/benchmark/fixtures/async-0.9.0.js
 create mode 100644 packages/context-coloring/benchmark/fixtures/jquery-2.1.1.js
 create mode 100644 packages/context-coloring/benchmark/fixtures/lodash-2.4.1.js
 create mode 100644 packages/context-coloring/benchmark/fixtures/mkdirp-0.5.0.js
 create mode 100644 packages/context-coloring/context-coloring.el
 create mode 100644 packages/context-coloring/scopifier.png
 create mode 100644 packages/context-coloring/screenshot.png
 create mode 100644 packages/context-coloring/scripts/download-dependencies.el
 create mode 100644 packages/context-coloring/test/context-coloring-test.el
 create mode 100644 packages/context-coloring/test/fixtures/block-scopes.js
 create mode 100644 packages/context-coloring/test/fixtures/catch.js
 create mode 100644 packages/context-coloring/test/fixtures/comments-and-strings.js
 create mode 100644 packages/context-coloring/test/fixtures/function-scopes.js
 create mode 100644 packages/context-coloring/test/fixtures/global.js
 create mode 100644 packages/context-coloring/test/fixtures/key-names.js
 create mode 100644 packages/context-coloring/test/fixtures/key-values.js
 create mode 100644 packages/context-coloring/test/fixtures/property-lookup.js

diff --git a/packages/context-coloring/.gitignore b/packages/context-coloring/.gitignore
new file mode 100644
index 0000000..f090318
--- /dev/null
+++ b/packages/context-coloring/.gitignore
@@ -0,0 +1,3 @@
+*.elc
+/benchmark/logs/
+/libraries/
diff --git a/packages/context-coloring/.travis.yml b/packages/context-coloring/.travis.yml
new file mode 100644
index 0000000..2dcc8a6
--- /dev/null
+++ b/packages/context-coloring/.travis.yml
@@ -0,0 +1,21 @@
+# https://github.com/rolandwalker/emacs-travis
+
+language: emacs-lisp
+
+node_js:
+  - "0.10"
+
+env:
+  matrix:
+    - EMACS=emacs24
+
+install:
+  - if [ "$EMACS" = "emacs24" ]; then
+        sudo add-apt-repository -y ppa:cassou/emacs &&
+        sudo apt-get update -qq &&
+        sudo apt-get install -qq emacs24 emacs24-el;
+    fi
+  - npm install -g scopifier
+
+script:
+  make test EMACS=${EMACS}
diff --git a/packages/context-coloring/Makefile b/packages/context-coloring/Makefile
new file mode 100644
index 0000000..2d37cd6
--- /dev/null
+++ b/packages/context-coloring/Makefile
@@ -0,0 +1,37 @@
+EMACS = emacs
+DEPENDENCIES = libraries/ert-async.el libraries/js2-mode.el
+
+all: clean compile test
+
+bench: ${DEPENDENCIES}
+	${EMACS} -Q \
+	-L . \
+	-L libraries \
+	-l context-coloring \
+	-l benchmark/context-coloring-benchmark \
+	-f context-coloring-benchmark-run
+
+compile: ${DEPENDENCIES}
+	${EMACS} -Q -batch \
+	-L . \
+	-L libraries \
+	-f batch-byte-compile *.el libraries/*.el
+
+clean:
+	rm -f *.elc libraries/*.elc ${DEPENDENCIES}
+
+${DEPENDENCIES}:
+	${EMACS} -Q -batch \
+	-l scripts/download-dependencies.el
+
+test: ${DEPENDENCIES}
+	${EMACS} -Q -batch \
+	-L . \
+	-L libraries \
+	-l ert \
+	-l ert-async \
+	-l context-coloring \
+	-l test/context-coloring-test.el \
+	-f ert-run-tests-batch-and-exit
+
+.PHONY: all bench compile clean test
diff --git a/packages/context-coloring/README.md b/packages/context-coloring/README.md
new file mode 100644
index 0000000..16b671a
--- /dev/null
+++ b/packages/context-coloring/README.md
@@ -0,0 +1,165 @@
+# Context Coloring [![Build Status](https://travis-ci.org/jacksonrayhamilton/context-coloring.png?branch=develop)](https://travis-ci.org/jacksonrayhamilton/context-coloring)
+
+<p align="center">
+  <img alt="Screenshot of JavaScript code highlighted by context." src="screenshot.png" title="Screenshot">
+</p>
+
+Highlights code according to function context.
+
+- Code in the global scope is one color. Code in functions within the global
+  scope is a different color, and code within such functions is another color,
+  and so on.
+- Identifiers retain the color of the scope in which they are declared.
+
+Lexical scope information at-a-glance can assist a programmer in understanding
+the overall structure of a program. It can help to curb nasty bugs like name
+shadowing. A rainbow can indicate excessive complexity. State change within a
+closure is easily monitored.
+
+By default, Context Coloring still highlights comments and strings
+syntactically. It is still easy to differentiate code from non-code, and strings
+cannot be confused for variables.
+
+This coloring strategy is probably more useful than conventional syntax
+highlighting. Highlighting keywords can help one to detect spelling errors, but
+a [linter][] could also spot those errors, and if integrated with [flycheck][],
+an extra spot opens up in your editing toolbelt.
+
+Give context coloring a try; you may find that it *changes the way you write
+code*.
+
+## Features
+
+- Supported languages: JavaScript
+- Light and dark (customizable) color schemes.
+- Very fast for files under 1000 lines.
+
+## Installation
+
+Requires Emacs 24+.
+
+JavaScript language support requires either [js2-mode][], or
+[Node.js 0.10+][node] and the [scopifier][] executable.
+
+```bash
+npm install -g scopifier
+```
+
+### ELPA
+
+- `M-x package-refresh-contents RET`
+- `M-x package-install RET context-coloring RET`
+
+### Git
+
+- Clone this repository.
+
+```bash
+cd ~/.emacs.d/
+git clone https://github.com/jacksonrayhamilton/context-coloring.git
+```
+
+- Byte-compile the package for improved speed.
+
+```bash
+cd context-coloring/
+make compile
+```
+
+- Add the following to your `~/.emacs` file:
+
+```lisp
+(add-to-list 'load-path "~/.emacs.d/context-coloring")
+(require 'context-coloring)
+(add-hook 'js2-mode-hook 'context-coloring-mode)
+```
+
+## Customizing
+
+Built-in themes are accessible via `context-coloring-load-theme`.  Available
+themes are: `monokai`, `solarized`, `tango` and `zenburn`.
+
+```lisp
+(require 'context-coloring)
+(context-coloring-load-theme 'zenburn)
+```
+
+You can define your own themes, too:
+
+```lisp
+(context-coloring-define-theme
+ 'zenburn
+ :colors '("#DCDCCC"
+           "#93E0E3"
+           "#BFEBBF"
+           "#F0DFAF"
+           "#DFAF8F"
+           "#CC9393"
+           "#DC8CC3"
+           "#94BFF3"
+           "#9FC59F"
+           "#D0BF8F"
+           "#DCA3A3"))
+```
+
+## Extending
+
+To add support for a new language, write a "scopifier" for it, and define a new
+coloring dispatch strategy with `context-coloring-define-dispatch`. Then the
+plugin should handle the rest.
+
+A "scopifier" is a CLI program that reads a buffer's contents from stdin and
+writes a JSON array of numbers to stdout. Every three numbers in the array
+represent a range of color. For instance, if I fed the following string of
+JavaScript code to a scopifier,
+
+```js
+var a = function () {};
+```
+
+then the scopifier would produce the following array:
+
+```js
+[1,24,0,9,23,1]
+```
+
+Where, for every three numbers, the first number is a 1-indexed start [point][],
+the second number is an exclusive end point, and the third number is a scope
+level. The result of applying level 0 coloring to the range &#91;1, 24) and then
+applying level 1 coloring to the range &#91;9, 23) would result in the following
+coloring:
+
+<p align="center">
+  <img alt="Screenshot of ranges &#91;1, 24) and &#91;9, 23)." src="scopifier.png" title="Screenshot">
+</p>
+
+If there is an abstract syntax tree generator for your language, you can walk
+the syntax tree, find variables and scopes, and build their positions and levels
+into an array like the one above.
+
+For example, a Ruby scopifier might be defined and implemented like this:
+
+```lisp
+(context-coloring-define-dispatch
+ 'ruby
+ :modes '(ruby-mode)
+ :executable "ruby"
+ :command "/home/username/scopifier")
+```
+
+```ruby
+#!/usr/bin/env ruby
+def scopifier(code)
+    # Parse code.
+    # Return an array.
+end
+print scopifier ARGF.read
+```
+
+[linter]: http://jshint.com/about/
+[flycheck]: http://www.flycheck.org/
+[zenburn]: http://github.com/bbatsov/zenburn-emacs
+[point]: http://www.gnu.org/software/emacs/manual/html_node/elisp/Point.html
+[js2-mode]: https://github.com/mooz/js2-mode
+[node]: http://nodejs.org/download/
+[load path]: https://www.gnu.org/software/emacs/manual/html_node/emacs/Lisp-Libraries.html
diff --git a/packages/context-coloring/benchmark/context-coloring-benchmark.el b/packages/context-coloring/benchmark/context-coloring-benchmark.el
new file mode 100644
index 0000000..004b66f
--- /dev/null
+++ b/packages/context-coloring/benchmark/context-coloring-benchmark.el
@@ -0,0 +1,160 @@
+;;; benchmark/context-coloring-benchmark.el --- Benchmarks for context coloring. -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2014-2015  Free Software Foundation, Inc.
+
+;; This file is part of GNU Emacs.
+
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+;;; Code:
+
+(defconst context-coloring-benchmark-path
+  (file-name-directory (or load-file-name buffer-file-name))
+  "This file's directory.")
+
+(defun context-coloring-benchmark-resolve-path (path)
+  "Resolve PATH from this file's directory."
+  (expand-file-name path context-coloring-benchmark-path))
+
+(defun context-coloring-benchmark-log-results (result-file fixture)
+  "Log benchmarking results for FIXTURE to RESULT-FILE."
+  (elp-results)
+  (let ((results-buffer (current-buffer)))
+    (with-temp-buffer
+      (insert (concat fixture "\n"))
+      (prepend-to-buffer results-buffer (point-min) (point-max)))
+    (with-temp-buffer
+      (insert "\n")
+      (append-to-buffer results-buffer (point-min) (point-max))))
+  (make-directory (context-coloring-benchmark-resolve-path "./logs") t)
+  (append-to-file nil nil result-file))
+
+(defun context-coloring-benchmark-next-tick (function)
+  "Defer execution of FUNCTION to clear the stack and to ensure
+asynchrony."
+  (run-at-time 0.001 nil function))
+
+(defun context-coloring-benchmark-next (list continue stop)
+  "Run the next test in LIST by calling CONTINUE.  When LIST is
+exhausted, call STOP instead."
+  (if (null list)
+      (context-coloring-benchmark-next-tick stop)
+    (context-coloring-benchmark-next-tick
+     (lambda ()
+       (funcall
+        continue
+        (car list)
+        (lambda ()
+          (context-coloring-benchmark-next (cdr list) continue stop)))))))
+
+(defun context-coloring-benchmark-async (title setup teardown fixtures callback)
+  "Measure the performance of all FIXTURES, calling CALLBACK when
+all are done."
+  (funcall setup)
+  (let ((result-file (context-coloring-benchmark-resolve-path
+                      (format "./logs/results-%s-%s.log"
+                              title (format-time-string "%s")))))
+    (context-coloring-benchmark-next
+     fixtures
+     (lambda (path next)
+       (let ((fixture (context-coloring-benchmark-resolve-path path))
+             advice)
+         (setq
+          advice
+          (let ((count 0))
+            (lambda (original-function)
+              (funcall
+               original-function
+               (lambda ()
+                 (setq count (+ count 1))
+                 ;; Test 5 times.
+                 (if (= count 5)
+                     (progn
+                       (advice-remove 'context-coloring-colorize advice)
+                       (kill-buffer)
+                       (context-coloring-benchmark-log-results
+                        result-file
+                        fixture)
+                       (funcall next))
+                   (funcall 'context-coloring-colorize)))))))
+         (advice-add 'context-coloring-colorize :around advice)
+         (find-file fixture)))
+     (lambda ()
+       (funcall teardown)
+       (if callback (funcall callback))))))
+
+(defconst context-coloring-benchmark-js-fixtures
+  '("./fixtures/jquery-2.1.1.js"
+    "./fixtures/lodash-2.4.1.js"
+    "./fixtures/async-0.9.0.js"
+    "./fixtures/mkdirp-0.5.0.js")
+  "Arbitrary JavaScript files for performance scrutiny.")
+
+(defun context-coloring-benchmark-js-mode-setup ()
+  "Preparation logic for `js-mode'."
+  (add-hook 'js-mode-hook 'context-coloring-mode)
+  (elp-instrument-package "context-coloring-"))
+
+(defun context-coloring-benchmark-js-mode-teardown ()
+  "Cleanup logic for `js-mode'."
+  (remove-hook 'js-mode-hook 'context-coloring-mode))
+
+(defun context-coloring-benchmark-js-mode-run (callback)
+  "Benchmark `js-mode', then call CALLBACK."
+  (context-coloring-benchmark-async
+   "js-mode"
+   'context-coloring-benchmark-js-mode-setup
+   'context-coloring-benchmark-js-mode-teardown
+   context-coloring-benchmark-js-fixtures
+   callback))
+
+(defun context-coloring-benchmark-js2-mode-setup ()
+  "Preparation logic for `js2-mode'."
+  (require 'js2-mode)
+  (setq js2-mode-show-parse-errors nil)
+  (setq js2-mode-show-strict-warnings nil)
+  (add-to-list 'auto-mode-alist '("\\.js\\'" . js2-mode))
+  (add-hook 'js2-mode-hook 'context-coloring-mode)
+  (elp-instrument-package "context-coloring-"))
+
+(defun context-coloring-benchmark-js2-mode-teardown ()
+  "Cleanup logic for `js2-mode'."
+  (remove-hook 'js2-mode-hook 'context-coloring-mode)
+  (setq auto-mode-alist (delete '("\\.js\\'" . js2-mode)
+                                auto-mode-alist))
+  (setq js2-mode-show-strict-warnings t)
+  (setq js2-mode-show-parse-errors t))
+
+(defun context-coloring-benchmark-js2-mode-run (callback)
+  "Benchmark `js2-mode', then call CALLBACK."
+  (context-coloring-benchmark-async
+   "js2-mode"
+   'context-coloring-benchmark-js2-mode-setup
+   'context-coloring-benchmark-js2-mode-teardown
+   context-coloring-benchmark-js-fixtures
+   callback))
+
+(defun context-coloring-benchmark-run ()
+  "Benchmark all modes, then exit."
+  (context-coloring-benchmark-next
+   '(context-coloring-benchmark-js-mode-run
+     context-coloring-benchmark-js2-mode-run)
+   (lambda (function next)
+     (funcall function next))
+   (lambda ()
+     (kill-emacs))))
+
+(provide 'context-coloring-benchmark)
+
+;;; context-coloring-benchmark.el ends here
diff --git a/packages/context-coloring/benchmark/fixtures/.dir-locals.el b/packages/context-coloring/benchmark/fixtures/.dir-locals.el
new file mode 100644
index 0000000..f3d5e97
--- /dev/null
+++ b/packages/context-coloring/benchmark/fixtures/.dir-locals.el
@@ -0,0 +1 @@
+((nil . ((buffer-read-only . t))))
diff --git a/packages/context-coloring/benchmark/fixtures/async-0.9.0.js b/packages/context-coloring/benchmark/fixtures/async-0.9.0.js
new file mode 100644
index 0000000..01e8afc
--- /dev/null
+++ b/packages/context-coloring/benchmark/fixtures/async-0.9.0.js
@@ -0,0 +1,1123 @@
+/*!
+ * async
+ * https://github.com/caolan/async
+ *
+ * Copyright 2010-2014 Caolan McMahon
+ * Released under the MIT license
+ */
+/*jshint onevar: false, indent:4 */
+/*global setImmediate: false, setTimeout: false, console: false */
+(function () {
+
+    var async = {};
+
+    // global on the server, window in the browser
+    var root, previous_async;
+
+    root = this;
+    if (root != null) {
+      previous_async = root.async;
+    }
+
+    async.noConflict = function () {
+        root.async = previous_async;
+        return async;
+    };
+
+    function only_once(fn) {
+        var called = false;
+        return function() {
+            if (called) throw new Error("Callback was already called.");
+            called = true;
+            fn.apply(root, arguments);
+        }
+    }
+
+    //// cross-browser compatiblity functions ////
+
+    var _toString = Object.prototype.toString;
+
+    var _isArray = Array.isArray || function (obj) {
+        return _toString.call(obj) === '[object Array]';
+    };
+
+    var _each = function (arr, iterator) {
+        if (arr.forEach) {
+            return arr.forEach(iterator);
+        }
+        for (var i = 0; i < arr.length; i += 1) {
+            iterator(arr[i], i, arr);
+        }
+    };
+
+    var _map = function (arr, iterator) {
+        if (arr.map) {
+            return arr.map(iterator);
+        }
+        var results = [];
+        _each(arr, function (x, i, a) {
+            results.push(iterator(x, i, a));
+        });
+        return results;
+    };
+
+    var _reduce = function (arr, iterator, memo) {
+        if (arr.reduce) {
+            return arr.reduce(iterator, memo);
+        }
+        _each(arr, function (x, i, a) {
+            memo = iterator(memo, x, i, a);
+        });
+        return memo;
+    };
+
+    var _keys = function (obj) {
+        if (Object.keys) {
+            return Object.keys(obj);
+        }
+        var keys = [];
+        for (var k in obj) {
+            if (obj.hasOwnProperty(k)) {
+                keys.push(k);
+            }
+        }
+        return keys;
+    };
+
+    //// exported async module functions ////
+
+    //// nextTick implementation with browser-compatible fallback ////
+    if (typeof process === 'undefined' || !(process.nextTick)) {
+        if (typeof setImmediate === 'function') {
+            async.nextTick = function (fn) {
+                // not a direct alias for IE10 compatibility
+                setImmediate(fn);
+            };
+            async.setImmediate = async.nextTick;
+        }
+        else {
+            async.nextTick = function (fn) {
+                setTimeout(fn, 0);
+            };
+            async.setImmediate = async.nextTick;
+        }
+    }
+    else {
+        async.nextTick = process.nextTick;
+        if (typeof setImmediate !== 'undefined') {
+            async.setImmediate = function (fn) {
+              // not a direct alias for IE10 compatibility
+              setImmediate(fn);
+            };
+        }
+        else {
+            async.setImmediate = async.nextTick;
+        }
+    }
+
+    async.each = function (arr, iterator, callback) {
+        callback = callback || function () {};
+        if (!arr.length) {
+            return callback();
+        }
+        var completed = 0;
+        _each(arr, function (x) {
+            iterator(x, only_once(done) );
+        });
+        function done(err) {
+          if (err) {
+              callback(err);
+              callback = function () {};
+          }
+          else {
+              completed += 1;
+              if (completed >= arr.length) {
+                  callback();
+              }
+          }
+        }
+    };
+    async.forEach = async.each;
+
+    async.eachSeries = function (arr, iterator, callback) {
+        callback = callback || function () {};
+        if (!arr.length) {
+            return callback();
+        }
+        var completed = 0;
+        var iterate = function () {
+            iterator(arr[completed], function (err) {
+                if (err) {
+                    callback(err);
+                    callback = function () {};
+                }
+                else {
+                    completed += 1;
+                    if (completed >= arr.length) {
+                        callback();
+                    }
+                    else {
+                        iterate();
+                    }
+                }
+            });
+        };
+        iterate();
+    };
+    async.forEachSeries = async.eachSeries;
+
+    async.eachLimit = function (arr, limit, iterator, callback) {
+        var fn = _eachLimit(limit);
+        fn.apply(null, [arr, iterator, callback]);
+    };
+    async.forEachLimit = async.eachLimit;
+
+    var _eachLimit = function (limit) {
+
+        return function (arr, iterator, callback) {
+            callback = callback || function () {};
+            if (!arr.length || limit <= 0) {
+                return callback();
+            }
+            var completed = 0;
+            var started = 0;
+            var running = 0;
+
+            (function replenish () {
+                if (completed >= arr.length) {
+                    return callback();
+                }
+
+                while (running < limit && started < arr.length) {
+                    started += 1;
+                    running += 1;
+                    iterator(arr[started - 1], function (err) {
+                        if (err) {
+                            callback(err);
+                            callback = function () {};
+                        }
+                        else {
+                            completed += 1;
+                            running -= 1;
+                            if (completed >= arr.length) {
+                                callback();
+                            }
+                            else {
+                                replenish();
+                            }
+                        }
+                    });
+                }
+            })();
+        };
+    };
+
+
+    var doParallel = function (fn) {
+        return function () {
+            var args = Array.prototype.slice.call(arguments);
+            return fn.apply(null, [async.each].concat(args));
+        };
+    };
+    var doParallelLimit = function(limit, fn) {
+        return function () {
+            var args = Array.prototype.slice.call(arguments);
+            return fn.apply(null, [_eachLimit(limit)].concat(args));
+        };
+    };
+    var doSeries = function (fn) {
+        return function () {
+            var args = Array.prototype.slice.call(arguments);
+            return fn.apply(null, [async.eachSeries].concat(args));
+        };
+    };
+
+
+    var _asyncMap = function (eachfn, arr, iterator, callback) {
+        arr = _map(arr, function (x, i) {
+            return {index: i, value: x};
+        });
+        if (!callback) {
+            eachfn(arr, function (x, callback) {
+                iterator(x.value, function (err) {
+                    callback(err);
+                });
+            });
+        } else {
+            var results = [];
+            eachfn(arr, function (x, callback) {
+                iterator(x.value, function (err, v) {
+                    results[x.index] = v;
+                    callback(err);
+                });
+            }, function (err) {
+                callback(err, results);
+            });
+        }
+    };
+    async.map = doParallel(_asyncMap);
+    async.mapSeries = doSeries(_asyncMap);
+    async.mapLimit = function (arr, limit, iterator, callback) {
+        return _mapLimit(limit)(arr, iterator, callback);
+    };
+
+    var _mapLimit = function(limit) {
+        return doParallelLimit(limit, _asyncMap);
+    };
+
+    // reduce only has a series version, as doing reduce in parallel won't
+    // work in many situations.
+    async.reduce = function (arr, memo, iterator, callback) {
+        async.eachSeries(arr, function (x, callback) {
+            iterator(memo, x, function (err, v) {
+                memo = v;
+                callback(err);
+            });
+        }, function (err) {
+            callback(err, memo);
+        });
+    };
+    // inject alias
+    async.inject = async.reduce;
+    // foldl alias
+    async.foldl = async.reduce;
+
+    async.reduceRight = function (arr, memo, iterator, callback) {
+        var reversed = _map(arr, function (x) {
+            return x;
+        }).reverse();
+        async.reduce(reversed, memo, iterator, callback);
+    };
+    // foldr alias
+    async.foldr = async.reduceRight;
+
+    var _filter = function (eachfn, arr, iterator, callback) {
+        var results = [];
+        arr = _map(arr, function (x, i) {
+            return {index: i, value: x};
+        });
+        eachfn(arr, function (x, callback) {
+            iterator(x.value, function (v) {
+                if (v) {
+                    results.push(x);
+                }
+                callback();
+            });
+        }, function (err) {
+            callback(_map(results.sort(function (a, b) {
+                return a.index - b.index;
+            }), function (x) {
+                return x.value;
+            }));
+        });
+    };
+    async.filter = doParallel(_filter);
+    async.filterSeries = doSeries(_filter);
+    // select alias
+    async.select = async.filter;
+    async.selectSeries = async.filterSeries;
+
+    var _reject = function (eachfn, arr, iterator, callback) {
+        var results = [];
+        arr = _map(arr, function (x, i) {
+            return {index: i, value: x};
+        });
+        eachfn(arr, function (x, callback) {
+            iterator(x.value, function (v) {
+                if (!v) {
+                    results.push(x);
+                }
+                callback();
+            });
+        }, function (err) {
+            callback(_map(results.sort(function (a, b) {
+                return a.index - b.index;
+            }), function (x) {
+                return x.value;
+            }));
+        });
+    };
+    async.reject = doParallel(_reject);
+    async.rejectSeries = doSeries(_reject);
+
+    var _detect = function (eachfn, arr, iterator, main_callback) {
+        eachfn(arr, function (x, callback) {
+            iterator(x, function (result) {
+                if (result) {
+                    main_callback(x);
+                    main_callback = function () {};
+                }
+                else {
+                    callback();
+                }
+            });
+        }, function (err) {
+            main_callback();
+        });
+    };
+    async.detect = doParallel(_detect);
+    async.detectSeries = doSeries(_detect);
+
+    async.some = function (arr, iterator, main_callback) {
+        async.each(arr, function (x, callback) {
+            iterator(x, function (v) {
+                if (v) {
+                    main_callback(true);
+                    main_callback = function () {};
+                }
+                callback();
+            });
+        }, function (err) {
+            main_callback(false);
+        });
+    };
+    // any alias
+    async.any = async.some;
+
+    async.every = function (arr, iterator, main_callback) {
+        async.each(arr, function (x, callback) {
+            iterator(x, function (v) {
+                if (!v) {
+                    main_callback(false);
+                    main_callback = function () {};
+                }
+                callback();
+            });
+        }, function (err) {
+            main_callback(true);
+        });
+    };
+    // all alias
+    async.all = async.every;
+
+    async.sortBy = function (arr, iterator, callback) {
+        async.map(arr, function (x, callback) {
+            iterator(x, function (err, criteria) {
+                if (err) {
+                    callback(err);
+                }
+                else {
+                    callback(null, {value: x, criteria: criteria});
+                }
+            });
+        }, function (err, results) {
+            if (err) {
+                return callback(err);
+            }
+            else {
+                var fn = function (left, right) {
+                    var a = left.criteria, b = right.criteria;
+                    return a < b ? -1 : a > b ? 1 : 0;
+                };
+                callback(null, _map(results.sort(fn), function (x) {
+                    return x.value;
+                }));
+            }
+        });
+    };
+
+    async.auto = function (tasks, callback) {
+        callback = callback || function () {};
+        var keys = _keys(tasks);
+        var remainingTasks = keys.length
+        if (!remainingTasks) {
+            return callback();
+        }
+
+        var results = {};
+
+        var listeners = [];
+        var addListener = function (fn) {
+            listeners.unshift(fn);
+        };
+        var removeListener = function (fn) {
+            for (var i = 0; i < listeners.length; i += 1) {
+                if (listeners[i] === fn) {
+                    listeners.splice(i, 1);
+                    return;
+                }
+            }
+        };
+        var taskComplete = function () {
+            remainingTasks--
+            _each(listeners.slice(0), function (fn) {
+                fn();
+            });
+        };
+
+        addListener(function () {
+            if (!remainingTasks) {
+                var theCallback = callback;
+                // prevent final callback from calling itself if it errors
+                callback = function () {};
+
+                theCallback(null, results);
+            }
+        });
+
+        _each(keys, function (k) {
+            var task = _isArray(tasks[k]) ? tasks[k]: [tasks[k]];
+            var taskCallback = function (err) {
+                var args = Array.prototype.slice.call(arguments, 1);
+                if (args.length <= 1) {
+                    args = args[0];
+                }
+                if (err) {
+                    var safeResults = {};
+                    _each(_keys(results), function(rkey) {
+                        safeResults[rkey] = results[rkey];
+                    });
+                    safeResults[k] = args;
+                    callback(err, safeResults);
+                    // stop subsequent errors hitting callback multiple times
+                    callback = function () {};
+                }
+                else {
+                    results[k] = args;
+                    async.setImmediate(taskComplete);
+                }
+            };
+            var requires = task.slice(0, Math.abs(task.length - 1)) || [];
+            var ready = function () {
+                return _reduce(requires, function (a, x) {
+                    return (a && results.hasOwnProperty(x));
+                }, true) && !results.hasOwnProperty(k);
+            };
+            if (ready()) {
+                task[task.length - 1](taskCallback, results);
+            }
+            else {
+                var listener = function () {
+                    if (ready()) {
+                        removeListener(listener);
+                        task[task.length - 1](taskCallback, results);
+                    }
+                };
+                addListener(listener);
+            }
+        });
+    };
+
+    async.retry = function(times, task, callback) {
+        var DEFAULT_TIMES = 5;
+        var attempts = [];
+        // Use defaults if times not passed
+        if (typeof times === 'function') {
+            callback = task;
+            task = times;
+            times = DEFAULT_TIMES;
+        }
+        // Make sure times is a number
+        times = parseInt(times, 10) || DEFAULT_TIMES;
+        var wrappedTask = function(wrappedCallback, wrappedResults) {
+            var retryAttempt = function(task, finalAttempt) {
+                return function(seriesCallback) {
+                    task(function(err, result){
+                        seriesCallback(!err || finalAttempt, {err: err, result: result});
+                    }, wrappedResults);
+                };
+            };
+            while (times) {
+                attempts.push(retryAttempt(task, !(times-=1)));
+            }
+            async.series(attempts, function(done, data){
+                data = data[data.length - 1];
+                (wrappedCallback || callback)(data.err, data.result);
+            });
+        }
+        // If a callback is passed, run this as a controll flow
+        return callback ? wrappedTask() : wrappedTask
+    };
+
+    async.waterfall = function (tasks, callback) {
+        callback = callback || function () {};
+        if (!_isArray(tasks)) {
+          var err = new Error('First argument to waterfall must be an array of functions');
+          return callback(err);
+        }
+        if (!tasks.length) {
+            return callback();
+        }
+        var wrapIterator = function (iterator) {
+            return function (err) {
+                if (err) {
+                    callback.apply(null, arguments);
+                    callback = function () {};
+                }
+                else {
+                    var args = Array.prototype.slice.call(arguments, 1);
+                    var next = iterator.next();
+                    if (next) {
+                        args.push(wrapIterator(next));
+                    }
+                    else {
+                        args.push(callback);
+                    }
+                    async.setImmediate(function () {
+                        iterator.apply(null, args);
+                    });
+                }
+            };
+        };
+        wrapIterator(async.iterator(tasks))();
+    };
+
+    var _parallel = function(eachfn, tasks, callback) {
+        callback = callback || function () {};
+        if (_isArray(tasks)) {
+            eachfn.map(tasks, function (fn, callback) {
+                if (fn) {
+                    fn(function (err) {
+                        var args = Array.prototype.slice.call(arguments, 1);
+                        if (args.length <= 1) {
+                            args = args[0];
+                        }
+                        callback.call(null, err, args);
+                    });
+                }
+            }, callback);
+        }
+        else {
+            var results = {};
+            eachfn.each(_keys(tasks), function (k, callback) {
+                tasks[k](function (err) {
+                    var args = Array.prototype.slice.call(arguments, 1);
+                    if (args.length <= 1) {
+                        args = args[0];
+                    }
+                    results[k] = args;
+                    callback(err);
+                });
+            }, function (err) {
+                callback(err, results);
+            });
+        }
+    };
+
+    async.parallel = function (tasks, callback) {
+        _parallel({ map: async.map, each: async.each }, tasks, callback);
+    };
+
+    async.parallelLimit = function(tasks, limit, callback) {
+        _parallel({ map: _mapLimit(limit), each: _eachLimit(limit) }, tasks, callback);
+    };
+
+    async.series = function (tasks, callback) {
+        callback = callback || function () {};
+        if (_isArray(tasks)) {
+            async.mapSeries(tasks, function (fn, callback) {
+                if (fn) {
+                    fn(function (err) {
+                        var args = Array.prototype.slice.call(arguments, 1);
+                        if (args.length <= 1) {
+                            args = args[0];
+                        }
+                        callback.call(null, err, args);
+                    });
+                }
+            }, callback);
+        }
+        else {
+            var results = {};
+            async.eachSeries(_keys(tasks), function (k, callback) {
+                tasks[k](function (err) {
+                    var args = Array.prototype.slice.call(arguments, 1);
+                    if (args.length <= 1) {
+                        args = args[0];
+                    }
+                    results[k] = args;
+                    callback(err);
+                });
+            }, function (err) {
+                callback(err, results);
+            });
+        }
+    };
+
+    async.iterator = function (tasks) {
+        var makeCallback = function (index) {
+            var fn = function () {
+                if (tasks.length) {
+                    tasks[index].apply(null, arguments);
+                }
+                return fn.next();
+            };
+            fn.next = function () {
+                return (index < tasks.length - 1) ? makeCallback(index + 1): null;
+            };
+            return fn;
+        };
+        return makeCallback(0);
+    };
+
+    async.apply = function (fn) {
+        var args = Array.prototype.slice.call(arguments, 1);
+        return function () {
+            return fn.apply(
+                null, args.concat(Array.prototype.slice.call(arguments))
+            );
+        };
+    };
+
+    var _concat = function (eachfn, arr, fn, callback) {
+        var r = [];
+        eachfn(arr, function (x, cb) {
+            fn(x, function (err, y) {
+                r = r.concat(y || []);
+                cb(err);
+            });
+        }, function (err) {
+            callback(err, r);
+        });
+    };
+    async.concat = doParallel(_concat);
+    async.concatSeries = doSeries(_concat);
+
+    async.whilst = function (test, iterator, callback) {
+        if (test()) {
+            iterator(function (err) {
+                if (err) {
+                    return callback(err);
+                }
+                async.whilst(test, iterator, callback);
+            });
+        }
+        else {
+            callback();
+        }
+    };
+
+    async.doWhilst = function (iterator, test, callback) {
+        iterator(function (err) {
+            if (err) {
+                return callback(err);
+            }
+            var args = Array.prototype.slice.call(arguments, 1);
+            if (test.apply(null, args)) {
+                async.doWhilst(iterator, test, callback);
+            }
+            else {
+                callback();
+            }
+        });
+    };
+
+    async.until = function (test, iterator, callback) {
+        if (!test()) {
+            iterator(function (err) {
+                if (err) {
+                    return callback(err);
+                }
+                async.until(test, iterator, callback);
+            });
+        }
+        else {
+            callback();
+        }
+    };
+
+    async.doUntil = function (iterator, test, callback) {
+        iterator(function (err) {
+            if (err) {
+                return callback(err);
+            }
+            var args = Array.prototype.slice.call(arguments, 1);
+            if (!test.apply(null, args)) {
+                async.doUntil(iterator, test, callback);
+            }
+            else {
+                callback();
+            }
+        });
+    };
+
+    async.queue = function (worker, concurrency) {
+        if (concurrency === undefined) {
+            concurrency = 1;
+        }
+        function _insert(q, data, pos, callback) {
+          if (!q.started){
+            q.started = true;
+          }
+          if (!_isArray(data)) {
+              data = [data];
+          }
+          if(data.length == 0) {
+             // call drain immediately if there are no tasks
+             return async.setImmediate(function() {
+                 if (q.drain) {
+                     q.drain();
+                 }
+             });
+          }
+          _each(data, function(task) {
+              var item = {
+                  data: task,
+                  callback: typeof callback === 'function' ? callback : null
+              };
+
+              if (pos) {
+                q.tasks.unshift(item);
+              } else {
+                q.tasks.push(item);
+              }
+
+              if (q.saturated && q.tasks.length === q.concurrency) {
+                  q.saturated();
+              }
+              async.setImmediate(q.process);
+          });
+        }
+
+        var workers = 0;
+        var q = {
+            tasks: [],
+            concurrency: concurrency,
+            saturated: null,
+            empty: null,
+            drain: null,
+            started: false,
+            paused: false,
+            push: function (data, callback) {
+              _insert(q, data, false, callback);
+            },
+            kill: function () {
+              q.drain = null;
+              q.tasks = [];
+            },
+            unshift: function (data, callback) {
+              _insert(q, data, true, callback);
+            },
+            process: function () {
+                if (!q.paused && workers < q.concurrency && q.tasks.length) {
+                    var task = q.tasks.shift();
+                    if (q.empty && q.tasks.length === 0) {
+                        q.empty();
+                    }
+                    workers += 1;
+                    var next = function () {
+                        workers -= 1;
+                        if (task.callback) {
+                            task.callback.apply(task, arguments);
+                        }
+                        if (q.drain && q.tasks.length + workers === 0) {
+                            q.drain();
+                        }
+                        q.process();
+                    };
+                    var cb = only_once(next);
+                    worker(task.data, cb);
+                }
+            },
+            length: function () {
+                return q.tasks.length;
+            },
+            running: function () {
+                return workers;
+            },
+            idle: function() {
+                return q.tasks.length + workers === 0;
+            },
+            pause: function () {
+                if (q.paused === true) { return; }
+                q.paused = true;
+                q.process();
+            },
+            resume: function () {
+                if (q.paused === false) { return; }
+                q.paused = false;
+                q.process();
+            }
+        };
+        return q;
+    };
+    
+    async.priorityQueue = function (worker, concurrency) {
+        
+        function _compareTasks(a, b){
+          return a.priority - b.priority;
+        };
+        
+        function _binarySearch(sequence, item, compare) {
+          var beg = -1,
+              end = sequence.length - 1;
+          while (beg < end) {
+            var mid = beg + ((end - beg + 1) >>> 1);
+            if (compare(item, sequence[mid]) >= 0) {
+              beg = mid;
+            } else {
+              end = mid - 1;
+            }
+          }
+          return beg;
+        }
+        
+        function _insert(q, data, priority, callback) {
+          if (!q.started){
+            q.started = true;
+          }
+          if (!_isArray(data)) {
+              data = [data];
+          }
+          if(data.length == 0) {
+             // call drain immediately if there are no tasks
+             return async.setImmediate(function() {
+                 if (q.drain) {
+                     q.drain();
+                 }
+             });
+          }
+          _each(data, function(task) {
+              var item = {
+                  data: task,
+                  priority: priority,
+                  callback: typeof callback === 'function' ? callback : null
+              };
+              
+              q.tasks.splice(_binarySearch(q.tasks, item, _compareTasks) + 1, 0, item);
+
+              if (q.saturated && q.tasks.length === q.concurrency) {
+                  q.saturated();
+              }
+              async.setImmediate(q.process);
+          });
+        }
+        
+        // Start with a normal queue
+        var q = async.queue(worker, concurrency);
+        
+        // Override push to accept second parameter representing priority
+        q.push = function (data, priority, callback) {
+          _insert(q, data, priority, callback);
+        };
+        
+        // Remove unshift function
+        delete q.unshift;
+
+        return q;
+    };
+
+    async.cargo = function (worker, payload) {
+        var working     = false,
+            tasks       = [];
+
+        var cargo = {
+            tasks: tasks,
+            payload: payload,
+            saturated: null,
+            empty: null,
+            drain: null,
+            drained: true,
+            push: function (data, callback) {
+                if (!_isArray(data)) {
+                    data = [data];
+                }
+                _each(data, function(task) {
+                    tasks.push({
+                        data: task,
+                        callback: typeof callback === 'function' ? callback : null
+                    });
+                    cargo.drained = false;
+                    if (cargo.saturated && tasks.length === payload) {
+                        cargo.saturated();
+                    }
+                });
+                async.setImmediate(cargo.process);
+            },
+            process: function process() {
+                if (working) return;
+                if (tasks.length === 0) {
+                    if(cargo.drain && !cargo.drained) cargo.drain();
+                    cargo.drained = true;
+                    return;
+                }
+
+                var ts = typeof payload === 'number'
+                            ? tasks.splice(0, payload)
+                            : tasks.splice(0, tasks.length);
+
+                var ds = _map(ts, function (task) {
+                    return task.data;
+                });
+
+                if(cargo.empty) cargo.empty();
+                working = true;
+                worker(ds, function () {
+                    working = false;
+
+                    var args = arguments;
+                    _each(ts, function (data) {
+                        if (data.callback) {
+                            data.callback.apply(null, args);
+                        }
+                    });
+
+                    process();
+                });
+            },
+            length: function () {
+                return tasks.length;
+            },
+            running: function () {
+                return working;
+            }
+        };
+        return cargo;
+    };
+
+    var _console_fn = function (name) {
+        return function (fn) {
+            var args = Array.prototype.slice.call(arguments, 1);
+            fn.apply(null, args.concat([function (err) {
+                var args = Array.prototype.slice.call(arguments, 1);
+                if (typeof console !== 'undefined') {
+                    if (err) {
+                        if (console.error) {
+                            console.error(err);
+                        }
+                    }
+                    else if (console[name]) {
+                        _each(args, function (x) {
+                            console[name](x);
+                        });
+                    }
+                }
+            }]));
+        };
+    };
+    async.log = _console_fn('log');
+    async.dir = _console_fn('dir');
+    /*async.info = _console_fn('info');
+    async.warn = _console_fn('warn');
+    async.error = _console_fn('error');*/
+
+    async.memoize = function (fn, hasher) {
+        var memo = {};
+        var queues = {};
+        hasher = hasher || function (x) {
+            return x;
+        };
+        var memoized = function () {
+            var args = Array.prototype.slice.call(arguments);
+            var callback = args.pop();
+            var key = hasher.apply(null, args);
+            if (key in memo) {
+                async.nextTick(function () {
+                    callback.apply(null, memo[key]);
+                });
+            }
+            else if (key in queues) {
+                queues[key].push(callback);
+            }
+            else {
+                queues[key] = [callback];
+                fn.apply(null, args.concat([function () {
+                    memo[key] = arguments;
+                    var q = queues[key];
+                    delete queues[key];
+                    for (var i = 0, l = q.length; i < l; i++) {
+                      q[i].apply(null, arguments);
+                    }
+                }]));
+            }
+        };
+        memoized.memo = memo;
+        memoized.unmemoized = fn;
+        return memoized;
+    };
+
+    async.unmemoize = function (fn) {
+      return function () {
+        return (fn.unmemoized || fn).apply(null, arguments);
+      };
+    };
+
+    async.times = function (count, iterator, callback) {
+        var counter = [];
+        for (var i = 0; i < count; i++) {
+            counter.push(i);
+        }
+        return async.map(counter, iterator, callback);
+    };
+
+    async.timesSeries = function (count, iterator, callback) {
+        var counter = [];
+        for (var i = 0; i < count; i++) {
+            counter.push(i);
+        }
+        return async.mapSeries(counter, iterator, callback);
+    };
+
+    async.seq = function (/* functions... */) {
+        var fns = arguments;
+        return function () {
+            var that = this;
+            var args = Array.prototype.slice.call(arguments);
+            var callback = args.pop();
+            async.reduce(fns, args, function (newargs, fn, cb) {
+                fn.apply(that, newargs.concat([function () {
+                    var err = arguments[0];
+                    var nextargs = Array.prototype.slice.call(arguments, 1);
+                    cb(err, nextargs);
+                }]))
+            },
+            function (err, results) {
+                callback.apply(that, [err].concat(results));
+            });
+        };
+    };
+
+    async.compose = function (/* functions... */) {
+      return async.seq.apply(null, Array.prototype.reverse.call(arguments));
+    };
+
+    var _applyEach = function (eachfn, fns /*args...*/) {
+        var go = function () {
+            var that = this;
+            var args = Array.prototype.slice.call(arguments);
+            var callback = args.pop();
+            return eachfn(fns, function (fn, cb) {
+                fn.apply(that, args.concat([cb]));
+            },
+            callback);
+        };
+        if (arguments.length > 2) {
+            var args = Array.prototype.slice.call(arguments, 2);
+            return go.apply(this, args);
+        }
+        else {
+            return go;
+        }
+    };
+    async.applyEach = doParallel(_applyEach);
+    async.applyEachSeries = doSeries(_applyEach);
+
+    async.forever = function (fn, callback) {
+        function next(err) {
+            if (err) {
+                if (callback) {
+                    return callback(err);
+                }
+                throw err;
+            }
+            fn(next);
+        }
+        next();
+    };
+
+    // Node.js
+    if (typeof module !== 'undefined' && module.exports) {
+        module.exports = async;
+    }
+    // AMD / RequireJS
+    else if (typeof define !== 'undefined' && define.amd) {
+        define([], function () {
+            return async;
+        });
+    }
+    // included directly via <script> tag
+    else {
+        root.async = async;
+    }
+
+}());
diff --git a/packages/context-coloring/benchmark/fixtures/jquery-2.1.1.js b/packages/context-coloring/benchmark/fixtures/jquery-2.1.1.js
new file mode 100644
index 0000000..9f7b3d3
--- /dev/null
+++ b/packages/context-coloring/benchmark/fixtures/jquery-2.1.1.js
@@ -0,0 +1,9190 @@
+/*!
+ * jQuery JavaScript Library v2.1.1
+ * http://jquery.com/
+ *
+ * Includes Sizzle.js
+ * http://sizzlejs.com/
+ *
+ * Copyright 2005, 2014 jQuery Foundation, Inc. and other contributors
+ * Released under the MIT license
+ * http://jquery.org/license
+ *
+ * Date: 2014-05-01T17:11Z
+ */
+
+(function( global, factory ) {
+
+	if ( typeof module === "object" && typeof module.exports === "object" ) {
+		// For CommonJS and CommonJS-like environments where a proper window is present,
+		// execute the factory and get jQuery
+		// For environments that do not inherently posses a window with a document
+		// (such as Node.js), expose a jQuery-making factory as module.exports
+		// This accentuates the need for the creation of a real window
+		// e.g. var jQuery = require("jquery")(window);
+		// See ticket #14549 for more info
+		module.exports = global.document ?
+			factory( global, true ) :
+			function( w ) {
+				if ( !w.document ) {
+					throw new Error( "jQuery requires a window with a document" );
+				}
+				return factory( w );
+			};
+	} else {
+		factory( global );
+	}
+
+// Pass this if window is not defined yet
+}(typeof window !== "undefined" ? window : this, function( window, noGlobal ) {
+
+// Can't do this because several apps including ASP.NET trace
+// the stack via arguments.caller.callee and Firefox dies if
+// you try to trace through "use strict" call chains. (#13335)
+// Support: Firefox 18+
+//
+
+var arr = [];
+
+var slice = arr.slice;
+
+var concat = arr.concat;
+
+var push = arr.push;
+
+var indexOf = arr.indexOf;
+
+var class2type = {};
+
+var toString = class2type.toString;
+
+var hasOwn = class2type.hasOwnProperty;
+
+var support = {};
+
+
+
+var
+	// Use the correct document accordingly with window argument (sandbox)
+	document = window.document,
+
+	version = "2.1.1",
+
+	// Define a local copy of jQuery
+	jQuery = function( selector, context ) {
+		// The jQuery object is actually just the init constructor 'enhanced'
+		// Need init if jQuery is called (just allow error to be thrown if not included)
+		return new jQuery.fn.init( selector, context );
+	},
+
+	// Support: Android<4.1
+	// Make sure we trim BOM and NBSP
+	rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,
+
+	// Matches dashed string for camelizing
+	rmsPrefix = /^-ms-/,
+	rdashAlpha = /-([\da-z])/gi,
+
+	// Used by jQuery.camelCase as callback to replace()
+	fcamelCase = function( all, letter ) {
+		return letter.toUpperCase();
+	};
+
+jQuery.fn = jQuery.prototype = {
+	// The current version of jQuery being used
+	jquery: version,
+
+	constructor: jQuery,
+
+	// Start with an empty selector
+	selector: "",
+
+	// The default length of a jQuery object is 0
+	length: 0,
+
+	toArray: function() {
+		return slice.call( this );
+	},
+
+	// Get the Nth element in the matched element set OR
+	// Get the whole matched element set as a clean array
+	get: function( num ) {
+		return num != null ?
+
+			// Return just the one element from the set
+			( num < 0 ? this[ num + this.length ] : this[ num ] ) :
+
+			// Return all the elements in a clean array
+			slice.call( this );
+	},
+
+	// Take an array of elements and push it onto the stack
+	// (returning the new matched element set)
+	pushStack: function( elems ) {
+
+		// Build a new jQuery matched element set
+		var ret = jQuery.merge( this.constructor(), elems );
+
+		// Add the old object onto the stack (as a reference)
+		ret.prevObject = this;
+		ret.context = this.context;
+
+		// Return the newly-formed element set
+		return ret;
+	},
+
+	// Execute a callback for every element in the matched set.
+	// (You can seed the arguments with an array of args, but this is
+	// only used internally.)
+	each: function( callback, args ) {
+		return jQuery.each( this, callback, args );
+	},
+
+	map: function( callback ) {
+		return this.pushStack( jQuery.map(this, function( elem, i ) {
+			return callback.call( elem, i, elem );
+		}));
+	},
+
+	slice: function() {
+		return this.pushStack( slice.apply( this, arguments ) );
+	},
+
+	first: function() {
+		return this.eq( 0 );
+	},
+
+	last: function() {
+		return this.eq( -1 );
+	},
+
+	eq: function( i ) {
+		var len = this.length,
+			j = +i + ( i < 0 ? len : 0 );
+		return this.pushStack( j >= 0 && j < len ? [ this[j] ] : [] );
+	},
+
+	end: function() {
+		return this.prevObject || this.constructor(null);
+	},
+
+	// For internal use only.
+	// Behaves like an Array's method, not like a jQuery method.
+	push: push,
+	sort: arr.sort,
+	splice: arr.splice
+};
+
+jQuery.extend = jQuery.fn.extend = function() {
+	var options, name, src, copy, copyIsArray, clone,
+		target = arguments[0] || {},
+		i = 1,
+		length = arguments.length,
+		deep = false;
+
+	// Handle a deep copy situation
+	if ( typeof target === "boolean" ) {
+		deep = target;
+
+		// skip the boolean and the target
+		target = arguments[ i ] || {};
+		i++;
+	}
+
+	// Handle case when target is a string or something (possible in deep copy)
+	if ( typeof target !== "object" && !jQuery.isFunction(target) ) {
+		target = {};
+	}
+
+	// extend jQuery itself if only one argument is passed
+	if ( i === length ) {
+		target = this;
+		i--;
+	}
+
+	for ( ; i < length; i++ ) {
+		// Only deal with non-null/undefined values
+		if ( (options = arguments[ i ]) != null ) {
+			// Extend the base object
+			for ( name in options ) {
+				src = target[ name ];
+				copy = options[ name ];
+
+				// Prevent never-ending loop
+				if ( target === copy ) {
+					continue;
+				}
+
+				// Recurse if we're merging plain objects or arrays
+				if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) {
+					if ( copyIsArray ) {
+						copyIsArray = false;
+						clone = src && jQuery.isArray(src) ? src : [];
+
+					} else {
+						clone = src && jQuery.isPlainObject(src) ? src : {};
+					}
+
+					// Never move original objects, clone them
+					target[ name ] = jQuery.extend( deep, clone, copy );
+
+				// Don't bring in undefined values
+				} else if ( copy !== undefined ) {
+					target[ name ] = copy;
+				}
+			}
+		}
+	}
+
+	// Return the modified object
+	return target;
+};
+
+jQuery.extend({
+	// Unique for each copy of jQuery on the page
+	expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ),
+
+	// Assume jQuery is ready without the ready module
+	isReady: true,
+
+	error: function( msg ) {
+		throw new Error( msg );
+	},
+
+	noop: function() {},
+
+	// See test/unit/core.js for details concerning isFunction.
+	// Since version 1.3, DOM methods and functions like alert
+	// aren't supported. They return false on IE (#2968).
+	isFunction: function( obj ) {
+		return jQuery.type(obj) === "function";
+	},
+
+	isArray: Array.isArray,
+
+	isWindow: function( obj ) {
+		return obj != null && obj === obj.window;
+	},
+
+	isNumeric: function( obj ) {
+		// parseFloat NaNs numeric-cast false positives (null|true|false|"")
+		// ...but misinterprets leading-number strings, particularly hex literals ("0x...")
+		// subtraction forces infinities to NaN
+		return !jQuery.isArray( obj ) && obj - parseFloat( obj ) >= 0;
+	},
+
+	isPlainObject: function( obj ) {
+		// Not plain objects:
+		// - Any object or value whose internal [[Class]] property is not "[object Object]"
+		// - DOM nodes
+		// - window
+		if ( jQuery.type( obj ) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) {
+			return false;
+		}
+
+		if ( obj.constructor &&
+				!hasOwn.call( obj.constructor.prototype, "isPrototypeOf" ) ) {
+			return false;
+		}
+
+		// If the function hasn't returned already, we're confident that
+		// |obj| is a plain object, created by {} or constructed with new Object
+		return true;
+	},
+
+	isEmptyObject: function( obj ) {
+		var name;
+		for ( name in obj ) {
+			return false;
+		}
+		return true;
+	},
+
+	type: function( obj ) {
+		if ( obj == null ) {
+			return obj + "";
+		}
+		// Support: Android < 4.0, iOS < 6 (functionish RegExp)
+		return typeof obj === "object" || typeof obj === "function" ?
+			class2type[ toString.call(obj) ] || "object" :
+			typeof obj;
+	},
+
+	// Evaluates a script in a global context
+	globalEval: function( code ) {
+		var script,
+			indirect = eval;
+
+		code = jQuery.trim( code );
+
+		if ( code ) {
+			// If the code includes a valid, prologue position
+			// strict mode pragma, execute code by injecting a
+			// script tag into the document.
+			if ( code.indexOf("use strict") === 1 ) {
+				script = document.createElement("script");
+				script.text = code;
+				document.head.appendChild( script ).parentNode.removeChild( script );
+			} else {
+			// Otherwise, avoid the DOM node creation, insertion
+			// and removal by using an indirect global eval
+				indirect( code );
+			}
+		}
+	},
+
+	// Convert dashed to camelCase; used by the css and data modules
+	// Microsoft forgot to hump their vendor prefix (#9572)
+	camelCase: function( string ) {
+		return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase );
+	},
+
+	nodeName: function( elem, name ) {
+		return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase();
+	},
+
+	// args is for internal usage only
+	each: function( obj, callback, args ) {
+		var value,
+			i = 0,
+			length = obj.length,
+			isArray = isArraylike( obj );
+
+		if ( args ) {
+			if ( isArray ) {
+				for ( ; i < length; i++ ) {
+					value = callback.apply( obj[ i ], args );
+
+					if ( value === false ) {
+						break;
+					}
+				}
+			} else {
+				for ( i in obj ) {
+					value = callback.apply( obj[ i ], args );
+
+					if ( value === false ) {
+						break;
+					}
+				}
+			}
+
+		// A special, fast, case for the most common use of each
+		} else {
+			if ( isArray ) {
+				for ( ; i < length; i++ ) {
+					value = callback.call( obj[ i ], i, obj[ i ] );
+
+					if ( value === false ) {
+						break;
+					}
+				}
+			} else {
+				for ( i in obj ) {
+					value = callback.call( obj[ i ], i, obj[ i ] );
+
+					if ( value === false ) {
+						break;
+					}
+				}
+			}
+		}
+
+		return obj;
+	},
+
+	// Support: Android<4.1
+	trim: function( text ) {
+		return text == null ?
+			"" :
+			( text + "" ).replace( rtrim, "" );
+	},
+
+	// results is for internal usage only
+	makeArray: function( arr, results ) {
+		var ret = results || [];
+
+		if ( arr != null ) {
+			if ( isArraylike( Object(arr) ) ) {
+				jQuery.merge( ret,
+					typeof arr === "string" ?
+					[ arr ] : arr
+				);
+			} else {
+				push.call( ret, arr );
+			}
+		}
+
+		return ret;
+	},
+
+	inArray: function( elem, arr, i ) {
+		return arr == null ? -1 : indexOf.call( arr, elem, i );
+	},
+
+	merge: function( first, second ) {
+		var len = +second.length,
+			j = 0,
+			i = first.length;
+
+		for ( ; j < len; j++ ) {
+			first[ i++ ] = second[ j ];
+		}
+
+		first.length = i;
+
+		return first;
+	},
+
+	grep: function( elems, callback, invert ) {
+		var callbackInverse,
+			matches = [],
+			i = 0,
+			length = elems.length,
+			callbackExpect = !invert;
+
+		// Go through the array, only saving the items
+		// that pass the validator function
+		for ( ; i < length; i++ ) {
+			callbackInverse = !callback( elems[ i ], i );
+			if ( callbackInverse !== callbackExpect ) {
+				matches.push( elems[ i ] );
+			}
+		}
+
+		return matches;
+	},
+
+	// arg is for internal usage only
+	map: function( elems, callback, arg ) {
+		var value,
+			i = 0,
+			length = elems.length,
+			isArray = isArraylike( elems ),
+			ret = [];
+
+		// Go through the array, translating each of the items to their new values
+		if ( isArray ) {
+			for ( ; i < length; i++ ) {
+				value = callback( elems[ i ], i, arg );
+
+				if ( value != null ) {
+					ret.push( value );
+				}
+			}
+
+		// Go through every key on the object,
+		} else {
+			for ( i in elems ) {
+				value = callback( elems[ i ], i, arg );
+
+				if ( value != null ) {
+					ret.push( value );
+				}
+			}
+		}
+
+		// Flatten any nested arrays
+		return concat.apply( [], ret );
+	},
+
+	// A global GUID counter for objects
+	guid: 1,
+
+	// Bind a function to a context, optionally partially applying any
+	// arguments.
+	proxy: function( fn, context ) {
+		var tmp, args, proxy;
+
+		if ( typeof context === "string" ) {
+			tmp = fn[ context ];
+			context = fn;
+			fn = tmp;
+		}
+
+		// Quick check to determine if target is callable, in the spec
+		// this throws a TypeError, but we will just return undefined.
+		if ( !jQuery.isFunction( fn ) ) {
+			return undefined;
+		}
+
+		// Simulated bind
+		args = slice.call( arguments, 2 );
+		proxy = function() {
+			return fn.apply( context || this, args.concat( slice.call( arguments ) ) );
+		};
+
+		// Set the guid of unique handler to the same of original handler, so it can be removed
+		proxy.guid = fn.guid = fn.guid || jQuery.guid++;
+
+		return proxy;
+	},
+
+	now: Date.now,
+
+	// jQuery.support is not used in Core but other projects attach their
+	// properties to it so it needs to exist.
+	support: support
+});
+
+// Populate the class2type map
+jQuery.each("Boolean Number String Function Array Date RegExp Object Error".split(" "), function(i, name) {
+	class2type[ "[object " + name + "]" ] = name.toLowerCase();
+});
+
+function isArraylike( obj ) {
+	var length = obj.length,
+		type = jQuery.type( obj );
+
+	if ( type === "function" || jQuery.isWindow( obj ) ) {
+		return false;
+	}
+
+	if ( obj.nodeType === 1 && length ) {
+		return true;
+	}
+
+	return type === "array" || length === 0 ||
+		typeof length === "number" && length > 0 && ( length - 1 ) in obj;
+}
+var Sizzle =
+/*!
+ * Sizzle CSS Selector Engine v1.10.19
+ * http://sizzlejs.com/
+ *
+ * Copyright 2013 jQuery Foundation, Inc. and other contributors
+ * Released under the MIT license
+ * http://jquery.org/license
+ *
+ * Date: 2014-04-18
+ */
+(function( window ) {
+
+var i,
+	support,
+	Expr,
+	getText,
+	isXML,
+	tokenize,
+	compile,
+	select,
+	outermostContext,
+	sortInput,
+	hasDuplicate,
+
+	// Local document vars
+	setDocument,
+	document,
+	docElem,
+	documentIsHTML,
+	rbuggyQSA,
+	rbuggyMatches,
+	matches,
+	contains,
+
+	// Instance-specific data
+	expando = "sizzle" + -(new Date()),
+	preferredDoc = window.document,
+	dirruns = 0,
+	done = 0,
+	classCache = createCache(),
+	tokenCache = createCache(),
+	compilerCache = createCache(),
+	sortOrder = function( a, b ) {
+		if ( a === b ) {
+			hasDuplicate = true;
+		}
+		return 0;
+	},
+
+	// General-purpose constants
+	strundefined = typeof undefined,
+	MAX_NEGATIVE = 1 << 31,
+
+	// Instance methods
+	hasOwn = ({}).hasOwnProperty,
+	arr = [],
+	pop = arr.pop,
+	push_native = arr.push,
+	push = arr.push,
+	slice = arr.slice,
+	// Use a stripped-down indexOf if we can't use a native one
+	indexOf = arr.indexOf || function( elem ) {
+		var i = 0,
+			len = this.length;
+		for ( ; i < len; i++ ) {
+			if ( this[i] === elem ) {
+				return i;
+			}
+		}
+		return -1;
+	},
+
+	booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",
+
+	// Regular expressions
+
+	// Whitespace characters http://www.w3.org/TR/css3-selectors/#whitespace
+	whitespace = "[\\x20\\t\\r\\n\\f]",
+	// http://www.w3.org/TR/css3-syntax/#characters
+	characterEncoding = "(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",
+
+	// Loosely modeled on CSS identifier characters
+	// An unquoted value should be a CSS identifier http://www.w3.org/TR/css3-selectors/#attribute-selectors
+	// Proper syntax: http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier
+	identifier = characterEncoding.replace( "w", "w#" ),
+
+	// Attribute selectors: http://www.w3.org/TR/selectors/#attribute-selectors
+	attributes = "\\[" + whitespace + "*(" + characterEncoding + ")(?:" + whitespace +
+		// Operator (capture 2)
+		"*([*^$|!~]?=)" + whitespace +
+		// "Attribute values must be CSS identifiers [capture 5] or strings [capture 3 or capture 4]"
+		"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" + whitespace +
+		"*\\]",
+
+	pseudos = ":(" + characterEncoding + ")(?:\\((" +
+		// To reduce the number of selectors needing tokenize in the preFilter, prefer arguments:
+		// 1. quoted (capture 3; capture 4 or capture 5)
+		"('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|" +
+		// 2. simple (capture 6)
+		"((?:\\\\.|[^\\\\()[\\]]|" + attributes + ")*)|" +
+		// 3. anything else (capture 2)
+		".*" +
+		")\\)|)",
+
+	// Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter
+	rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ),
+
+	rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ),
+	rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*" ),
+
+	rattributeQuotes = new RegExp( "=" + whitespace + "*([^\\]'\"]*?)" + whitespace + "*\\]", "g" ),
+
+	rpseudo = new RegExp( pseudos ),
+	ridentifier = new RegExp( "^" + identifier + "$" ),
+
+	matchExpr = {
+		"ID": new RegExp( "^#(" + characterEncoding + ")" ),
+		"CLASS": new RegExp( "^\\.(" + characterEncoding + ")" ),
+		"TAG": new RegExp( "^(" + characterEncoding.replace( "w", "w*" ) + ")" ),
+		"ATTR": new RegExp( "^" + attributes ),
+		"PSEUDO": new RegExp( "^" + pseudos ),
+		"CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace +
+			"*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace +
+			"*(\\d+)|))" + whitespace + "*\\)|)", "i" ),
+		"bool": new RegExp( "^(?:" + booleans + ")$", "i" ),
+		// For use in libraries implementing .is()
+		// We use this for POS matching in `select`
+		"needsContext": new RegExp( "^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" +
+			whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" )
+	},
+
+	rinputs = /^(?:input|select|textarea|button)$/i,
+	rheader = /^h\d$/i,
+
+	rnative = /^[^{]+\{\s*\[native \w/,
+
+	// Easily-parseable/retrievable ID or TAG or CLASS selectors
+	rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,
+
+	rsibling = /[+~]/,
+	rescape = /'|\\/g,
+
+	// CSS escapes http://www.w3.org/TR/CSS21/syndata.html#escaped-characters
+	runescape = new RegExp( "\\\\([\\da-f]{1,6}" + whitespace + "?|(" + whitespace + ")|.)", "ig" ),
+	funescape = function( _, escaped, escapedWhitespace ) {
+		var high = "0x" + escaped - 0x10000;
+		// NaN means non-codepoint
+		// Support: Firefox<24
+		// Workaround erroneous numeric interpretation of +"0x"
+		return high !== high || escapedWhitespace ?
+			escaped :
+			high < 0 ?
+				// BMP codepoint
+				String.fromCharCode( high + 0x10000 ) :
+				// Supplemental Plane codepoint (surrogate pair)
+				String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 );
+	};
+
+// Optimize for push.apply( _, NodeList )
+try {
+	push.apply(
+		(arr = slice.call( preferredDoc.childNodes )),
+		preferredDoc.childNodes
+	);
+	// Support: Android<4.0
+	// Detect silently failing push.apply
+	arr[ preferredDoc.childNodes.length ].nodeType;
+} catch ( e ) {
+	push = { apply: arr.length ?
+
+		// Leverage slice if possible
+		function( target, els ) {
+			push_native.apply( target, slice.call(els) );
+		} :
+
+		// Support: IE<9
+		// Otherwise append directly
+		function( target, els ) {
+			var j = target.length,
+				i = 0;
+			// Can't trust NodeList.length
+			while ( (target[j++] = els[i++]) ) {}
+			target.length = j - 1;
+		}
+	};
+}
+
+function Sizzle( selector, context, results, seed ) {
+	var match, elem, m, nodeType,
+		// QSA vars
+		i, groups, old, nid, newContext, newSelector;
+
+	if ( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) {
+		setDocument( context );
+	}
+
+	context = context || document;
+	results = results || [];
+
+	if ( !selector || typeof selector !== "string" ) {
+		return results;
+	}
+
+	if ( (nodeType = context.nodeType) !== 1 && nodeType !== 9 ) {
+		return [];
+	}
+
+	if ( documentIsHTML && !seed ) {
+
+		// Shortcuts
+		if ( (match = rquickExpr.exec( selector )) ) {
+			// Speed-up: Sizzle("#ID")
+			if ( (m = match[1]) ) {
+				if ( nodeType === 9 ) {
+					elem = context.getElementById( m );
+					// Check parentNode to catch when Blackberry 4.6 returns
+					// nodes that are no longer in the document (jQuery #6963)
+					if ( elem && elem.parentNode ) {
+						// Handle the case where IE, Opera, and Webkit return items
+						// by name instead of ID
+						if ( elem.id === m ) {
+							results.push( elem );
+							return results;
+						}
+					} else {
+						return results;
+					}
+				} else {
+					// Context is not a document
+					if ( context.ownerDocument && (elem = context.ownerDocument.getElementById( m )) &&
+						contains( context, elem ) && elem.id === m ) {
+						results.push( elem );
+						return results;
+					}
+				}
+
+			// Speed-up: Sizzle("TAG")
+			} else if ( match[2] ) {
+				push.apply( results, context.getElementsByTagName( selector ) );
+				return results;
+
+			// Speed-up: Sizzle(".CLASS")
+			} else if ( (m = match[3]) && support.getElementsByClassName && context.getElementsByClassName ) {
+				push.apply( results, context.getElementsByClassName( m ) );
+				return results;
+			}
+		}
+
+		// QSA path
+		if ( support.qsa && (!rbuggyQSA || !rbuggyQSA.test( selector )) ) {
+			nid = old = expando;
+			newContext = context;
+			newSelector = nodeType === 9 && selector;
+
+			// qSA works strangely on Element-rooted queries
+			// We can work around this by specifying an extra ID on the root
+			// and working up from there (Thanks to Andrew Dupont for the technique)
+			// IE 8 doesn't work on object elements
+			if ( nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) {
+				groups = tokenize( selector );
+
+				if ( (old = context.getAttribute("id")) ) {
+					nid = old.replace( rescape, "\\$&" );
+				} else {
+					context.setAttribute( "id", nid );
+				}
+				nid = "[id='" + nid + "'] ";
+
+				i = groups.length;
+				while ( i-- ) {
+					groups[i] = nid + toSelector( groups[i] );
+				}
+				newContext = rsibling.test( selector ) && testContext( context.parentNode ) || context;
+				newSelector = groups.join(",");
+			}
+
+			if ( newSelector ) {
+				try {
+					push.apply( results,
+						newContext.querySelectorAll( newSelector )
+					);
+					return results;
+				} catch(qsaError) {
+				} finally {
+					if ( !old ) {
+						context.removeAttribute("id");
+					}
+				}
+			}
+		}
+	}
+
+	// All others
+	return select( selector.replace( rtrim, "$1" ), context, results, seed );
+}
+
+/**
+ * Create key-value caches of limited size
+ * @returns {Function(string, Object)} Returns the Object data after storing it on itself with
+ *	property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength)
+ *	deleting the oldest entry
+ */
+function createCache() {
+	var keys = [];
+
+	function cache( key, value ) {
+		// Use (key + " ") to avoid collision with native prototype properties (see Issue #157)
+		if ( keys.push( key + " " ) > Expr.cacheLength ) {
+			// Only keep the most recent entries
+			delete cache[ keys.shift() ];
+		}
+		return (cache[ key + " " ] = value);
+	}
+	return cache;
+}
+
+/**
+ * Mark a function for special use by Sizzle
+ * @param {Function} fn The function to mark
+ */
+function markFunction( fn ) {
+	fn[ expando ] = true;
+	return fn;
+}
+
+/**
+ * Support testing using an element
+ * @param {Function} fn Passed the created div and expects a boolean result
+ */
+function assert( fn ) {
+	var div = document.createElement("div");
+
+	try {
+		return !!fn( div );
+	} catch (e) {
+		return false;
+	} finally {
+		// Remove from its parent by default
+		if ( div.parentNode ) {
+			div.parentNode.removeChild( div );
+		}
+		// release memory in IE
+		div = null;
+	}
+}
+
+/**
+ * Adds the same handler for all of the specified attrs
+ * @param {String} attrs Pipe-separated list of attributes
+ * @param {Function} handler The method that will be applied
+ */
+function addHandle( attrs, handler ) {
+	var arr = attrs.split("|"),
+		i = attrs.length;
+
+	while ( i-- ) {
+		Expr.attrHandle[ arr[i] ] = handler;
+	}
+}
+
+/**
+ * Checks document order of two siblings
+ * @param {Element} a
+ * @param {Element} b
+ * @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b
+ */
+function siblingCheck( a, b ) {
+	var cur = b && a,
+		diff = cur && a.nodeType === 1 && b.nodeType === 1 &&
+			( ~b.sourceIndex || MAX_NEGATIVE ) -
+			( ~a.sourceIndex || MAX_NEGATIVE );
+
+	// Use IE sourceIndex if available on both nodes
+	if ( diff ) {
+		return diff;
+	}
+
+	// Check if b follows a
+	if ( cur ) {
+		while ( (cur = cur.nextSibling) ) {
+			if ( cur === b ) {
+				return -1;
+			}
+		}
+	}
+
+	return a ? 1 : -1;
+}
+
+/**
+ * Returns a function to use in pseudos for input types
+ * @param {String} type
+ */
+function createInputPseudo( type ) {
+	return function( elem ) {
+		var name = elem.nodeName.toLowerCase();
+		return name === "input" && elem.type === type;
+	};
+}
+
+/**
+ * Returns a function to use in pseudos for buttons
+ * @param {String} type
+ */
+function createButtonPseudo( type ) {
+	return function( elem ) {
+		var name = elem.nodeName.toLowerCase();
+		return (name === "input" || name === "button") && elem.type === type;
+	};
+}
+
+/**
+ * Returns a function to use in pseudos for positionals
+ * @param {Function} fn
+ */
+function createPositionalPseudo( fn ) {
+	return markFunction(function( argument ) {
+		argument = +argument;
+		return markFunction(function( seed, matches ) {
+			var j,
+				matchIndexes = fn( [], seed.length, argument ),
+				i = matchIndexes.length;
+
+			// Match elements found at the specified indexes
+			while ( i-- ) {
+				if ( seed[ (j = matchIndexes[i]) ] ) {
+					seed[j] = !(matches[j] = seed[j]);
+				}
+			}
+		});
+	});
+}
+
+/**
+ * Checks a node for validity as a Sizzle context
+ * @param {Element|Object=} context
+ * @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value
+ */
+function testContext( context ) {
+	return context && typeof context.getElementsByTagName !== strundefined && context;
+}
+
+// Expose support vars for convenience
+support = Sizzle.support = {};
+
+/**
+ * Detects XML nodes
+ * @param {Element|Object} elem An element or a document
+ * @returns {Boolean} True iff elem is a non-HTML XML node
+ */
+isXML = Sizzle.isXML = function( elem ) {
+	// documentElement is verified for cases where it doesn't yet exist
+	// (such as loading iframes in IE - #4833)
+	var documentElement = elem && (elem.ownerDocument || elem).documentElement;
+	return documentElement ? documentElement.nodeName !== "HTML" : false;
+};
+
+/**
+ * Sets document-related variables once based on the current document
+ * @param {Element|Object} [doc] An element or document object to use to set the document
+ * @returns {Object} Returns the current document
+ */
+setDocument = Sizzle.setDocument = function( node ) {
+	var hasCompare,
+		doc = node ? node.ownerDocument || node : preferredDoc,
+		parent = doc.defaultView;
+
+	// If no document and documentElement is available, return
+	if ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) {
+		return document;
+	}
+
+	// Set our document
+	document = doc;
+	docElem = doc.documentElement;
+
+	// Support tests
+	documentIsHTML = !isXML( doc );
+
+	// Support: IE>8
+	// If iframe document is assigned to "document" variable and if iframe has been reloaded,
+	// IE will throw "permission denied" error when accessing "document" variable, see jQuery #13936
+	// IE6-8 do not support the defaultView property so parent will be undefined
+	if ( parent && parent !== parent.top ) {
+		// IE11 does not have attachEvent, so all must suffer
+		if ( parent.addEventListener ) {
+			parent.addEventListener( "unload", function() {
+				setDocument();
+			}, false );
+		} else if ( parent.attachEvent ) {
+			parent.attachEvent( "onunload", function() {
+				setDocument();
+			});
+		}
+	}
+
+	/* Attributes
+	---------------------------------------------------------------------- */
+
+	// Support: IE<8
+	// Verify that getAttribute really returns attributes and not properties (excepting IE8 booleans)
+	support.attributes = assert(function( div ) {
+		div.className = "i";
+		return !div.getAttribute("className");
+	});
+
+	/* getElement(s)By*
+	---------------------------------------------------------------------- */
+
+	// Check if getElementsByTagName("*") returns only elements
+	support.getElementsByTagName = assert(function( div ) {
+		div.appendChild( doc.createComment("") );
+		return !div.getElementsByTagName("*").length;
+	});
+
+	// Check if getElementsByClassName can be trusted
+	support.getElementsByClassName = rnative.test( doc.getElementsByClassName ) && assert(function( div ) {
+		div.innerHTML = "<div class='a'></div><div class='a i'></div>";
+
+		// Support: Safari<4
+		// Catch class over-caching
+		div.firstChild.className = "i";
+		// Support: Opera<10
+		// Catch gEBCN failure to find non-leading classes
+		return div.getElementsByClassName("i").length === 2;
+	});
+
+	// Support: IE<10
+	// Check if getElementById returns elements by name
+	// The broken getElementById methods don't pick up programatically-set names,
+	// so use a roundabout getElementsByName test
+	support.getById = assert(function( div ) {
+		docElem.appendChild( div ).id = expando;
+		return !doc.getElementsByName || !doc.getElementsByName( expando ).length;
+	});
+
+	// ID find and filter
+	if ( support.getById ) {
+		Expr.find["ID"] = function( id, context ) {
+			if ( typeof context.getElementById !== strundefined && documentIsHTML ) {
+				var m = context.getElementById( id );
+				// Check parentNode to catch when Blackberry 4.6 returns
+				// nodes that are no longer in the document #6963
+				return m && m.parentNode ? [ m ] : [];
+			}
+		};
+		Expr.filter["ID"] = function( id ) {
+			var attrId = id.replace( runescape, funescape );
+			return function( elem ) {
+				return elem.getAttribute("id") === attrId;
+			};
+		};
+	} else {
+		// Support: IE6/7
+		// getElementById is not reliable as a find shortcut
+		delete Expr.find["ID"];
+
+		Expr.filter["ID"] =  function( id ) {
+			var attrId = id.replace( runescape, funescape );
+			return function( elem ) {
+				var node = typeof elem.getAttributeNode !== strundefined && elem.getAttributeNode("id");
+				return node && node.value === attrId;
+			};
+		};
+	}
+
+	// Tag
+	Expr.find["TAG"] = support.getElementsByTagName ?
+		function( tag, context ) {
+			if ( typeof context.getElementsByTagName !== strundefined ) {
+				return context.getElementsByTagName( tag );
+			}
+		} :
+		function( tag, context ) {
+			var elem,
+				tmp = [],
+				i = 0,
+				results = context.getElementsByTagName( tag );
+
+			// Filter out possible comments
+			if ( tag === "*" ) {
+				while ( (elem = results[i++]) ) {
+					if ( elem.nodeType === 1 ) {
+						tmp.push( elem );
+					}
+				}
+
+				return tmp;
+			}
+			return results;
+		};
+
+	// Class
+	Expr.find["CLASS"] = support.getElementsByClassName && function( className, context ) {
+		if ( typeof context.getElementsByClassName !== strundefined && documentIsHTML ) {
+			return context.getElementsByClassName( className );
+		}
+	};
+
+	/* QSA/matchesSelector
+	---------------------------------------------------------------------- */
+
+	// QSA and matchesSelector support
+
+	// matchesSelector(:active) reports false when true (IE9/Opera 11.5)
+	rbuggyMatches = [];
+
+	// qSa(:focus) reports false when true (Chrome 21)
+	// We allow this because of a bug in IE8/9 that throws an error
+	// whenever `document.activeElement` is accessed on an iframe
+	// So, we allow :focus to pass through QSA all the time to avoid the IE error
+	// See http://bugs.jquery.com/ticket/13378
+	rbuggyQSA = [];
+
+	if ( (support.qsa = rnative.test( doc.querySelectorAll )) ) {
+		// Build QSA regex
+		// Regex strategy adopted from Diego Perini
+		assert(function( div ) {
+			// Select is set to empty string on purpose
+			// This is to test IE's treatment of not explicitly
+			// setting a boolean content attribute,
+			// since its presence should be enough
+			// http://bugs.jquery.com/ticket/12359
+			div.innerHTML = "<select msallowclip=''><option selected=''></option></select>";
+
+			// Support: IE8, Opera 11-12.16
+			// Nothing should be selected when empty strings follow ^= or $= or *=
+			// The test attribute must be unknown in Opera but "safe" for WinRT
+			// http://msdn.microsoft.com/en-us/library/ie/hh465388.aspx#attribute_section
+			if ( div.querySelectorAll("[msallowclip^='']").length ) {
+				rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" );
+			}
+
+			// Support: IE8
+			// Boolean attributes and "value" are not treated correctly
+			if ( !div.querySelectorAll("[selected]").length ) {
+				rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" );
+			}
+
+			// Webkit/Opera - :checked should return selected option elements
+			// http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
+			// IE8 throws error here and will not see later tests
+			if ( !div.querySelectorAll(":checked").length ) {
+				rbuggyQSA.push(":checked");
+			}
+		});
+
+		assert(function( div ) {
+			// Support: Windows 8 Native Apps
+			// The type and name attributes are restricted during .innerHTML assignment
+			var input = doc.createElement("input");
+			input.setAttribute( "type", "hidden" );
+			div.appendChild( input ).setAttribute( "name", "D" );
+
+			// Support: IE8
+			// Enforce case-sensitivity of name attribute
+			if ( div.querySelectorAll("[name=d]").length ) {
+				rbuggyQSA.push( "name" + whitespace + "*[*^$|!~]?=" );
+			}
+
+			// FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled)
+			// IE8 throws error here and will not see later tests
+			if ( !div.querySelectorAll(":enabled").length ) {
+				rbuggyQSA.push( ":enabled", ":disabled" );
+			}
+
+			// Opera 10-11 does not throw on post-comma invalid pseudos
+			div.querySelectorAll("*,:x");
+			rbuggyQSA.push(",.*:");
+		});
+	}
+
+	if ( (support.matchesSelector = rnative.test( (matches = docElem.matches ||
+		docElem.webkitMatchesSelector ||
+		docElem.mozMatchesSelector ||
+		docElem.oMatchesSelector ||
+		docElem.msMatchesSelector) )) ) {
+
+		assert(function( div ) {
+			// Check to see if it's possible to do matchesSelector
+			// on a disconnected node (IE 9)
+			support.disconnectedMatch = matches.call( div, "div" );
+
+			// This should fail with an exception
+			// Gecko does not error, returns false instead
+			matches.call( div, "[s!='']:x" );
+			rbuggyMatches.push( "!=", pseudos );
+		});
+	}
+
+	rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join("|") );
+	rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join("|") );
+
+	/* Contains
+	---------------------------------------------------------------------- */
+	hasCompare = rnative.test( docElem.compareDocumentPosition );
+
+	// Element contains another
+	// Purposefully does not implement inclusive descendent
+	// As in, an element does not contain itself
+	contains = hasCompare || rnative.test( docElem.contains ) ?
+		function( a, b ) {
+			var adown = a.nodeType === 9 ? a.documentElement : a,
+				bup = b && b.parentNode;
+			return a === bup || !!( bup && bup.nodeType === 1 && (
+				adown.contains ?
+					adown.contains( bup ) :
+					a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16
+			));
+		} :
+		function( a, b ) {
+			if ( b ) {
+				while ( (b = b.parentNode) ) {
+					if ( b === a ) {
+						return true;
+					}
+				}
+			}
+			return false;
+		};
+
+	/* Sorting
+	---------------------------------------------------------------------- */
+
+	// Document order sorting
+	sortOrder = hasCompare ?
+	function( a, b ) {
+
+		// Flag for duplicate removal
+		if ( a === b ) {
+			hasDuplicate = true;
+			return 0;
+		}
+
+		// Sort on method existence if only one input has compareDocumentPosition
+		var compare = !a.compareDocumentPosition - !b.compareDocumentPosition;
+		if ( compare ) {
+			return compare;
+		}
+
+		// Calculate position if both inputs belong to the same document
+		compare = ( a.ownerDocument || a ) === ( b.ownerDocument || b ) ?
+			a.compareDocumentPosition( b ) :
+
+			// Otherwise we know they are disconnected
+			1;
+
+		// Disconnected nodes
+		if ( compare & 1 ||
+			(!support.sortDetached && b.compareDocumentPosition( a ) === compare) ) {
+
+			// Choose the first element that is related to our preferred document
+			if ( a === doc || a.ownerDocument === preferredDoc && contains(preferredDoc, a) ) {
+				return -1;
+			}
+			if ( b === doc || b.ownerDocument === preferredDoc && contains(preferredDoc, b) ) {
+				return 1;
+			}
+
+			// Maintain original order
+			return sortInput ?
+				( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) :
+				0;
+		}
+
+		return compare & 4 ? -1 : 1;
+	} :
+	function( a, b ) {
+		// Exit early if the nodes are identical
+		if ( a === b ) {
+			hasDuplicate = true;
+			return 0;
+		}
+
+		var cur,
+			i = 0,
+			aup = a.parentNode,
+			bup = b.parentNode,
+			ap = [ a ],
+			bp = [ b ];
+
+		// Parentless nodes are either documents or disconnected
+		if ( !aup || !bup ) {
+			return a === doc ? -1 :
+				b === doc ? 1 :
+				aup ? -1 :
+				bup ? 1 :
+				sortInput ?
+				( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) :
+				0;
+
+		// If the nodes are siblings, we can do a quick check
+		} else if ( aup === bup ) {
+			return siblingCheck( a, b );
+		}
+
+		// Otherwise we need full lists of their ancestors for comparison
+		cur = a;
+		while ( (cur = cur.parentNode) ) {
+			ap.unshift( cur );
+		}
+		cur = b;
+		while ( (cur = cur.parentNode) ) {
+			bp.unshift( cur );
+		}
+
+		// Walk down the tree looking for a discrepancy
+		while ( ap[i] === bp[i] ) {
+			i++;
+		}
+
+		return i ?
+			// Do a sibling check if the nodes have a common ancestor
+			siblingCheck( ap[i], bp[i] ) :
+
+			// Otherwise nodes in our document sort first
+			ap[i] === preferredDoc ? -1 :
+			bp[i] === preferredDoc ? 1 :
+			0;
+	};
+
+	return doc;
+};
+
+Sizzle.matches = function( expr, elements ) {
+	return Sizzle( expr, null, null, elements );
+};
+
+Sizzle.matchesSelector = function( elem, expr ) {
+	// Set document vars if needed
+	if ( ( elem.ownerDocument || elem ) !== document ) {
+		setDocument( elem );
+	}
+
+	// Make sure that attribute selectors are quoted
+	expr = expr.replace( rattributeQuotes, "='$1']" );
+
+	if ( support.matchesSelector && documentIsHTML &&
+		( !rbuggyMatches || !rbuggyMatches.test( expr ) ) &&
+		( !rbuggyQSA     || !rbuggyQSA.test( expr ) ) ) {
+
+		try {
+			var ret = matches.call( elem, expr );
+
+			// IE 9's matchesSelector returns false on disconnected nodes
+			if ( ret || support.disconnectedMatch ||
+					// As well, disconnected nodes are said to be in a document
+					// fragment in IE 9
+					elem.document && elem.document.nodeType !== 11 ) {
+				return ret;
+			}
+		} catch(e) {}
+	}
+
+	return Sizzle( expr, document, null, [ elem ] ).length > 0;
+};
+
+Sizzle.contains = function( context, elem ) {
+	// Set document vars if needed
+	if ( ( context.ownerDocument || context ) !== document ) {
+		setDocument( context );
+	}
+	return contains( context, elem );
+};
+
+Sizzle.attr = function( elem, name ) {
+	// Set document vars if needed
+	if ( ( elem.ownerDocument || elem ) !== document ) {
+		setDocument( elem );
+	}
+
+	var fn = Expr.attrHandle[ name.toLowerCase() ],
+		// Don't get fooled by Object.prototype properties (jQuery #13807)
+		val = fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ?
+			fn( elem, name, !documentIsHTML ) :
+			undefined;
+
+	return val !== undefined ?
+		val :
+		support.attributes || !documentIsHTML ?
+			elem.getAttribute( name ) :
+			(val = elem.getAttributeNode(name)) && val.specified ?
+				val.value :
+				null;
+};
+
+Sizzle.error = function( msg ) {
+	throw new Error( "Syntax error, unrecognized expression: " + msg );
+};
+
+/**
+ * Document sorting and removing duplicates
+ * @param {ArrayLike} results
+ */
+Sizzle.uniqueSort = function( results ) {
+	var elem,
+		duplicates = [],
+		j = 0,
+		i = 0;
+
+	// Unless we *know* we can detect duplicates, assume their presence
+	hasDuplicate = !support.detectDuplicates;
+	sortInput = !support.sortStable && results.slice( 0 );
+	results.sort( sortOrder );
+
+	if ( hasDuplicate ) {
+		while ( (elem = results[i++]) ) {
+			if ( elem === results[ i ] ) {
+				j = duplicates.push( i );
+			}
+		}
+		while ( j-- ) {
+			results.splice( duplicates[ j ], 1 );
+		}
+	}
+
+	// Clear input after sorting to release objects
+	// See https://github.com/jquery/sizzle/pull/225
+	sortInput = null;
+
+	return results;
+};
+
+/**
+ * Utility function for retrieving the text value of an array of DOM nodes
+ * @param {Array|Element} elem
+ */
+getText = Sizzle.getText = function( elem ) {
+	var node,
+		ret = "",
+		i = 0,
+		nodeType = elem.nodeType;
+
+	if ( !nodeType ) {
+		// If no nodeType, this is expected to be an array
+		while ( (node = elem[i++]) ) {
+			// Do not traverse comment nodes
+			ret += getText( node );
+		}
+	} else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) {
+		// Use textContent for elements
+		// innerText usage removed for consistency of new lines (jQuery #11153)
+		if ( typeof elem.textContent === "string" ) {
+			return elem.textContent;
+		} else {
+			// Traverse its children
+			for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {
+				ret += getText( elem );
+			}
+		}
+	} else if ( nodeType === 3 || nodeType === 4 ) {
+		return elem.nodeValue;
+	}
+	// Do not include comment or processing instruction nodes
+
+	return ret;
+};
+
+Expr = Sizzle.selectors = {
+
+	// Can be adjusted by the user
+	cacheLength: 50,
+
+	createPseudo: markFunction,
+
+	match: matchExpr,
+
+	attrHandle: {},
+
+	find: {},
+
+	relative: {
+		">": { dir: "parentNode", first: true },
+		" ": { dir: "parentNode" },
+		"+": { dir: "previousSibling", first: true },
+		"~": { dir: "previousSibling" }
+	},
+
+	preFilter: {
+		"ATTR": function( match ) {
+			match[1] = match[1].replace( runescape, funescape );
+
+			// Move the given value to match[3] whether quoted or unquoted
+			match[3] = ( match[3] || match[4] || match[5] || "" ).replace( runescape, funescape );
+
+			if ( match[2] === "~=" ) {
+				match[3] = " " + match[3] + " ";
+			}
+
+			return match.slice( 0, 4 );
+		},
+
+		"CHILD": function( match ) {
+			/* matches from matchExpr["CHILD"]
+				1 type (only|nth|...)
+				2 what (child|of-type)
+				3 argument (even|odd|\d*|\d*n([+-]\d+)?|...)
+				4 xn-component of xn+y argument ([+-]?\d*n|)
+				5 sign of xn-component
+				6 x of xn-component
+				7 sign of y-component
+				8 y of y-component
+			*/
+			match[1] = match[1].toLowerCase();
+
+			if ( match[1].slice( 0, 3 ) === "nth" ) {
+				// nth-* requires argument
+				if ( !match[3] ) {
+					Sizzle.error( match[0] );
+				}
+
+				// numeric x and y parameters for Expr.filter.CHILD
+				// remember that false/true cast respectively to 0/1
+				match[4] = +( match[4] ? match[5] + (match[6] || 1) : 2 * ( match[3] === "even" || match[3] === "odd" ) );
+				match[5] = +( ( match[7] + match[8] ) || match[3] === "odd" );
+
+			// other types prohibit arguments
+			} else if ( match[3] ) {
+				Sizzle.error( match[0] );
+			}
+
+			return match;
+		},
+
+		"PSEUDO": function( match ) {
+			var excess,
+				unquoted = !match[6] && match[2];
+
+			if ( matchExpr["CHILD"].test( match[0] ) ) {
+				return null;
+			}
+
+			// Accept quoted arguments as-is
+			if ( match[3] ) {
+				match[2] = match[4] || match[5] || "";
+
+			// Strip excess characters from unquoted arguments
+			} else if ( unquoted && rpseudo.test( unquoted ) &&
+				// Get excess from tokenize (recursively)
+				(excess = tokenize( unquoted, true )) &&
+				// advance to the next closing parenthesis
+				(excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length) ) {
+
+				// excess is a negative index
+				match[0] = match[0].slice( 0, excess );
+				match[2] = unquoted.slice( 0, excess );
+			}
+
+			// Return only captures needed by the pseudo filter method (type and argument)
+			return match.slice( 0, 3 );
+		}
+	},
+
+	filter: {
+
+		"TAG": function( nodeNameSelector ) {
+			var nodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase();
+			return nodeNameSelector === "*" ?
+				function() { return true; } :
+				function( elem ) {
+					return elem.nodeName && elem.nodeName.toLowerCase() === nodeName;
+				};
+		},
+
+		"CLASS": function( className ) {
+			var pattern = classCache[ className + " " ];
+
+			return pattern ||
+				(pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) &&
+				classCache( className, function( elem ) {
+					return pattern.test( typeof elem.className === "string" && elem.className || typeof elem.getAttribute !== strundefined && elem.getAttribute("class") || "" );
+				});
+		},
+
+		"ATTR": function( name, operator, check ) {
+			return function( elem ) {
+				var result = Sizzle.attr( elem, name );
+
+				if ( result == null ) {
+					return operator === "!=";
+				}
+				if ( !operator ) {
+					return true;
+				}
+
+				result += "";
+
+				return operator === "=" ? result === check :
+					operator === "!=" ? result !== check :
+					operator === "^=" ? check && result.indexOf( check ) === 0 :
+					operator === "*=" ? check && result.indexOf( check ) > -1 :
+					operator === "$=" ? check && result.slice( -check.length ) === check :
+					operator === "~=" ? ( " " + result + " " ).indexOf( check ) > -1 :
+					operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" :
+					false;
+			};
+		},
+
+		"CHILD": function( type, what, argument, first, last ) {
+			var simple = type.slice( 0, 3 ) !== "nth",
+				forward = type.slice( -4 ) !== "last",
+				ofType = what === "of-type";
+
+			return first === 1 && last === 0 ?
+
+				// Shortcut for :nth-*(n)
+				function( elem ) {
+					return !!elem.parentNode;
+				} :
+
+				function( elem, context, xml ) {
+					var cache, outerCache, node, diff, nodeIndex, start,
+						dir = simple !== forward ? "nextSibling" : "previousSibling",
+						parent = elem.parentNode,
+						name = ofType && elem.nodeName.toLowerCase(),
+						useCache = !xml && !ofType;
+
+					if ( parent ) {
+
+						// :(first|last|only)-(child|of-type)
+						if ( simple ) {
+							while ( dir ) {
+								node = elem;
+								while ( (node = node[ dir ]) ) {
+									if ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) {
+										return false;
+									}
+								}
+								// Reverse direction for :only-* (if we haven't yet done so)
+								start = dir = type === "only" && !start && "nextSibling";
+							}
+							return true;
+						}
+
+						start = [ forward ? parent.firstChild : parent.lastChild ];
+
+						// non-xml :nth-child(...) stores cache data on `parent`
+						if ( forward && useCache ) {
+							// Seek `elem` from a previously-cached index
+							outerCache = parent[ expando ] || (parent[ expando ] = {});
+							cache = outerCache[ type ] || [];
+							nodeIndex = cache[0] === dirruns && cache[1];
+							diff = cache[0] === dirruns && cache[2];
+							node = nodeIndex && parent.childNodes[ nodeIndex ];
+
+							while ( (node = ++nodeIndex && node && node[ dir ] ||
+
+								// Fallback to seeking `elem` from the start
+								(diff = nodeIndex = 0) || start.pop()) ) {
+
+								// When found, cache indexes on `parent` and break
+								if ( node.nodeType === 1 && ++diff && node === elem ) {
+									outerCache[ type ] = [ dirruns, nodeIndex, diff ];
+									break;
+								}
+							}
+
+						// Use previously-cached element index if available
+						} else if ( useCache && (cache = (elem[ expando ] || (elem[ expando ] = {}))[ type ]) && cache[0] === dirruns ) {
+							diff = cache[1];
+
+						// xml :nth-child(...) or :nth-last-child(...) or :nth(-last)?-of-type(...)
+						} else {
+							// Use the same loop as above to seek `elem` from the start
+							while ( (node = ++nodeIndex && node && node[ dir ] ||
+								(diff = nodeIndex = 0) || start.pop()) ) {
+
+								if ( ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) && ++diff ) {
+									// Cache the index of each encountered element
+									if ( useCache ) {
+										(node[ expando ] || (node[ expando ] = {}))[ type ] = [ dirruns, diff ];
+									}
+
+									if ( node === elem ) {
+										break;
+									}
+								}
+							}
+						}
+
+						// Incorporate the offset, then check against cycle size
+						diff -= last;
+						return diff === first || ( diff % first === 0 && diff / first >= 0 );
+					}
+				};
+		},
+
+		"PSEUDO": function( pseudo, argument ) {
+			// pseudo-class names are case-insensitive
+			// http://www.w3.org/TR/selectors/#pseudo-classes
+			// Prioritize by case sensitivity in case custom pseudos are added with uppercase letters
+			// Remember that setFilters inherits from pseudos
+			var args,
+				fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] ||
+					Sizzle.error( "unsupported pseudo: " + pseudo );
+
+			// The user may use createPseudo to indicate that
+			// arguments are needed to create the filter function
+			// just as Sizzle does
+			if ( fn[ expando ] ) {
+				return fn( argument );
+			}
+
+			// But maintain support for old signatures
+			if ( fn.length > 1 ) {
+				args = [ pseudo, pseudo, "", argument ];
+				return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ?
+					markFunction(function( seed, matches ) {
+						var idx,
+							matched = fn( seed, argument ),
+							i = matched.length;
+						while ( i-- ) {
+							idx = indexOf.call( seed, matched[i] );
+							seed[ idx ] = !( matches[ idx ] = matched[i] );
+						}
+					}) :
+					function( elem ) {
+						return fn( elem, 0, args );
+					};
+			}
+
+			return fn;
+		}
+	},
+
+	pseudos: {
+		// Potentially complex pseudos
+		"not": markFunction(function( selector ) {
+			// Trim the selector passed to compile
+			// to avoid treating leading and trailing
+			// spaces as combinators
+			var input = [],
+				results = [],
+				matcher = compile( selector.replace( rtrim, "$1" ) );
+
+			return matcher[ expando ] ?
+				markFunction(function( seed, matches, context, xml ) {
+					var elem,
+						unmatched = matcher( seed, null, xml, [] ),
+						i = seed.length;
+
+					// Match elements unmatched by `matcher`
+					while ( i-- ) {
+						if ( (elem = unmatched[i]) ) {
+							seed[i] = !(matches[i] = elem);
+						}
+					}
+				}) :
+				function( elem, context, xml ) {
+					input[0] = elem;
+					matcher( input, null, xml, results );
+					return !results.pop();
+				};
+		}),
+
+		"has": markFunction(function( selector ) {
+			return function( elem ) {
+				return Sizzle( selector, elem ).length > 0;
+			};
+		}),
+
+		"contains": markFunction(function( text ) {
+			return function( elem ) {
+				return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1;
+			};
+		}),
+
+		// "Whether an element is represented by a :lang() selector
+		// is based solely on the element's language value
+		// being equal to the identifier C,
+		// or beginning with the identifier C immediately followed by "-".
+		// The matching of C against the element's language value is performed case-insensitively.
+		// The identifier C does not have to be a valid language name."
+		// http://www.w3.org/TR/selectors/#lang-pseudo
+		"lang": markFunction( function( lang ) {
+			// lang value must be a valid identifier
+			if ( !ridentifier.test(lang || "") ) {
+				Sizzle.error( "unsupported lang: " + lang );
+			}
+			lang = lang.replace( runescape, funescape ).toLowerCase();
+			return function( elem ) {
+				var elemLang;
+				do {
+					if ( (elemLang = documentIsHTML ?
+						elem.lang :
+						elem.getAttribute("xml:lang") || elem.getAttribute("lang")) ) {
+
+						elemLang = elemLang.toLowerCase();
+						return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0;
+					}
+				} while ( (elem = elem.parentNode) && elem.nodeType === 1 );
+				return false;
+			};
+		}),
+
+		// Miscellaneous
+		"target": function( elem ) {
+			var hash = window.location && window.location.hash;
+			return hash && hash.slice( 1 ) === elem.id;
+		},
+
+		"root": function( elem ) {
+			return elem === docElem;
+		},
+
+		"focus": function( elem ) {
+			return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex);
+		},
+
+		// Boolean properties
+		"enabled": function( elem ) {
+			return elem.disabled === false;
+		},
+
+		"disabled": function( elem ) {
+			return elem.disabled === true;
+		},
+
+		"checked": function( elem ) {
+			// In CSS3, :checked should return both checked and selected elements
+			// http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
+			var nodeName = elem.nodeName.toLowerCase();
+			return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected);
+		},
+
+		"selected": function( elem ) {
+			// Accessing this property makes selected-by-default
+			// options in Safari work properly
+			if ( elem.parentNode ) {
+				elem.parentNode.selectedIndex;
+			}
+
+			return elem.selected === true;
+		},
+
+		// Contents
+		"empty": function( elem ) {
+			// http://www.w3.org/TR/selectors/#empty-pseudo
+			// :empty is negated by element (1) or content nodes (text: 3; cdata: 4; entity ref: 5),
+			//   but not by others (comment: 8; processing instruction: 7; etc.)
+			// nodeType < 6 works because attributes (2) do not appear as children
+			for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {
+				if ( elem.nodeType < 6 ) {
+					return false;
+				}
+			}
+			return true;
+		},
+
+		"parent": function( elem ) {
+			return !Expr.pseudos["empty"]( elem );
+		},
+
+		// Element/input types
+		"header": function( elem ) {
+			return rheader.test( elem.nodeName );
+		},
+
+		"input": function( elem ) {
+			return rinputs.test( elem.nodeName );
+		},
+
+		"button": function( elem ) {
+			var name = elem.nodeName.toLowerCase();
+			return name === "input" && elem.type === "button" || name === "button";
+		},
+
+		"text": function( elem ) {
+			var attr;
+			return elem.nodeName.toLowerCase() === "input" &&
+				elem.type === "text" &&
+
+				// Support: IE<8
+				// New HTML5 attribute values (e.g., "search") appear with elem.type === "text"
+				( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === "text" );
+		},
+
+		// Position-in-collection
+		"first": createPositionalPseudo(function() {
+			return [ 0 ];
+		}),
+
+		"last": createPositionalPseudo(function( matchIndexes, length ) {
+			return [ length - 1 ];
+		}),
+
+		"eq": createPositionalPseudo(function( matchIndexes, length, argument ) {
+			return [ argument < 0 ? argument + length : argument ];
+		}),
+
+		"even": createPositionalPseudo(function( matchIndexes, length ) {
+			var i = 0;
+			for ( ; i < length; i += 2 ) {
+				matchIndexes.push( i );
+			}
+			return matchIndexes;
+		}),
+
+		"odd": createPositionalPseudo(function( matchIndexes, length ) {
+			var i = 1;
+			for ( ; i < length; i += 2 ) {
+				matchIndexes.push( i );
+			}
+			return matchIndexes;
+		}),
+
+		"lt": createPositionalPseudo(function( matchIndexes, length, argument ) {
+			var i = argument < 0 ? argument + length : argument;
+			for ( ; --i >= 0; ) {
+				matchIndexes.push( i );
+			}
+			return matchIndexes;
+		}),
+
+		"gt": createPositionalPseudo(function( matchIndexes, length, argument ) {
+			var i = argument < 0 ? argument + length : argument;
+			for ( ; ++i < length; ) {
+				matchIndexes.push( i );
+			}
+			return matchIndexes;
+		})
+	}
+};
+
+Expr.pseudos["nth"] = Expr.pseudos["eq"];
+
+// Add button/input type pseudos
+for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) {
+	Expr.pseudos[ i ] = createInputPseudo( i );
+}
+for ( i in { submit: true, reset: true } ) {
+	Expr.pseudos[ i ] = createButtonPseudo( i );
+}
+
+// Easy API for creating new setFilters
+function setFilters() {}
+setFilters.prototype = Expr.filters = Expr.pseudos;
+Expr.setFilters = new setFilters();
+
+tokenize = Sizzle.tokenize = function( selector, parseOnly ) {
+	var matched, match, tokens, type,
+		soFar, groups, preFilters,
+		cached = tokenCache[ selector + " " ];
+
+	if ( cached ) {
+		return parseOnly ? 0 : cached.slice( 0 );
+	}
+
+	soFar = selector;
+	groups = [];
+	preFilters = Expr.preFilter;
+
+	while ( soFar ) {
+
+		// Comma and first run
+		if ( !matched || (match = rcomma.exec( soFar )) ) {
+			if ( match ) {
+				// Don't consume trailing commas as valid
+				soFar = soFar.slice( match[0].length ) || soFar;
+			}
+			groups.push( (tokens = []) );
+		}
+
+		matched = false;
+
+		// Combinators
+		if ( (match = rcombinators.exec( soFar )) ) {
+			matched = match.shift();
+			tokens.push({
+				value: matched,
+				// Cast descendant combinators to space
+				type: match[0].replace( rtrim, " " )
+			});
+			soFar = soFar.slice( matched.length );
+		}
+
+		// Filters
+		for ( type in Expr.filter ) {
+			if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] ||
+				(match = preFilters[ type ]( match ))) ) {
+				matched = match.shift();
+				tokens.push({
+					value: matched,
+					type: type,
+					matches: match
+				});
+				soFar = soFar.slice( matched.length );
+			}
+		}
+
+		if ( !matched ) {
+			break;
+		}
+	}
+
+	// Return the length of the invalid excess
+	// if we're just parsing
+	// Otherwise, throw an error or return tokens
+	return parseOnly ?
+		soFar.length :
+		soFar ?
+			Sizzle.error( selector ) :
+			// Cache the tokens
+			tokenCache( selector, groups ).slice( 0 );
+};
+
+function toSelector( tokens ) {
+	var i = 0,
+		len = tokens.length,
+		selector = "";
+	for ( ; i < len; i++ ) {
+		selector += tokens[i].value;
+	}
+	return selector;
+}
+
+function addCombinator( matcher, combinator, base ) {
+	var dir = combinator.dir,
+		checkNonElements = base && dir === "parentNode",
+		doneName = done++;
+
+	return combinator.first ?
+		// Check against closest ancestor/preceding element
+		function( elem, context, xml ) {
+			while ( (elem = elem[ dir ]) ) {
+				if ( elem.nodeType === 1 || checkNonElements ) {
+					return matcher( elem, context, xml );
+				}
+			}
+		} :
+
+		// Check against all ancestor/preceding elements
+		function( elem, context, xml ) {
+			var oldCache, outerCache,
+				newCache = [ dirruns, doneName ];
+
+			// We can't set arbitrary data on XML nodes, so they don't benefit from dir caching
+			if ( xml ) {
+				while ( (elem = elem[ dir ]) ) {
+					if ( elem.nodeType === 1 || checkNonElements ) {
+						if ( matcher( elem, context, xml ) ) {
+							return true;
+						}
+					}
+				}
+			} else {
+				while ( (elem = elem[ dir ]) ) {
+					if ( elem.nodeType === 1 || checkNonElements ) {
+						outerCache = elem[ expando ] || (elem[ expando ] = {});
+						if ( (oldCache = outerCache[ dir ]) &&
+							oldCache[ 0 ] === dirruns && oldCache[ 1 ] === doneName ) {
+
+							// Assign to newCache so results back-propagate to previous elements
+							return (newCache[ 2 ] = oldCache[ 2 ]);
+						} else {
+							// Reuse newcache so results back-propagate to previous elements
+							outerCache[ dir ] = newCache;
+
+							// A match means we're done; a fail means we have to keep checking
+							if ( (newCache[ 2 ] = matcher( elem, context, xml )) ) {
+								return true;
+							}
+						}
+					}
+				}
+			}
+		};
+}
+
+function elementMatcher( matchers ) {
+	return matchers.length > 1 ?
+		function( elem, context, xml ) {
+			var i = matchers.length;
+			while ( i-- ) {
+				if ( !matchers[i]( elem, context, xml ) ) {
+					return false;
+				}
+			}
+			return true;
+		} :
+		matchers[0];
+}
+
+function multipleContexts( selector, contexts, results ) {
+	var i = 0,
+		len = contexts.length;
+	for ( ; i < len; i++ ) {
+		Sizzle( selector, contexts[i], results );
+	}
+	return results;
+}
+
+function condense( unmatched, map, filter, context, xml ) {
+	var elem,
+		newUnmatched = [],
+		i = 0,
+		len = unmatched.length,
+		mapped = map != null;
+
+	for ( ; i < len; i++ ) {
+		if ( (elem = unmatched[i]) ) {
+			if ( !filter || filter( elem, context, xml ) ) {
+				newUnmatched.push( elem );
+				if ( mapped ) {
+					map.push( i );
+				}
+			}
+		}
+	}
+
+	return newUnmatched;
+}
+
+function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) {
+	if ( postFilter && !postFilter[ expando ] ) {
+		postFilter = setMatcher( postFilter );
+	}
+	if ( postFinder && !postFinder[ expando ] ) {
+		postFinder = setMatcher( postFinder, postSelector );
+	}
+	return markFunction(function( seed, results, context, xml ) {
+		var temp, i, elem,
+			preMap = [],
+			postMap = [],
+			preexisting = results.length,
+
+			// Get initial elements from seed or context
+			elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [] ),
+
+			// Prefilter to get matcher input, preserving a map for seed-results synchronization
+			matcherIn = preFilter && ( seed || !selector ) ?
+				condense( elems, preMap, preFilter, context, xml ) :
+				elems,
+
+			matcherOut = matcher ?
+				// If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results,
+				postFinder || ( seed ? preFilter : preexisting || postFilter ) ?
+
+					// ...intermediate processing is necessary
+					[] :
+
+					// ...otherwise use results directly
+					results :
+				matcherIn;
+
+		// Find primary matches
+		if ( matcher ) {
+			matcher( matcherIn, matcherOut, context, xml );
+		}
+
+		// Apply postFilter
+		if ( postFilter ) {
+			temp = condense( matcherOut, postMap );
+			postFilter( temp, [], context, xml );
+
+			// Un-match failing elements by moving them back to matcherIn
+			i = temp.length;
+			while ( i-- ) {
+				if ( (elem = temp[i]) ) {
+					matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem);
+				}
+			}
+		}
+
+		if ( seed ) {
+			if ( postFinder || preFilter ) {
+				if ( postFinder ) {
+					// Get the final matcherOut by condensing this intermediate into postFinder contexts
+					temp = [];
+					i = matcherOut.length;
+					while ( i-- ) {
+						if ( (elem = matcherOut[i]) ) {
+							// Restore matcherIn since elem is not yet a final match
+							temp.push( (matcherIn[i] = elem) );
+						}
+					}
+					postFinder( null, (matcherOut = []), temp, xml );
+				}
+
+				// Move matched elements from seed to results to keep them synchronized
+				i = matcherOut.length;
+				while ( i-- ) {
+					if ( (elem = matcherOut[i]) &&
+						(temp = postFinder ? indexOf.call( seed, elem ) : preMap[i]) > -1 ) {
+
+						seed[temp] = !(results[temp] = elem);
+					}
+				}
+			}
+
+		// Add elements to results, through postFinder if defined
+		} else {
+			matcherOut = condense(
+				matcherOut === results ?
+					matcherOut.splice( preexisting, matcherOut.length ) :
+					matcherOut
+			);
+			if ( postFinder ) {
+				postFinder( null, results, matcherOut, xml );
+			} else {
+				push.apply( results, matcherOut );
+			}
+		}
+	});
+}
+
+function matcherFromTokens( tokens ) {
+	var checkContext, matcher, j,
+		len = tokens.length,
+		leadingRelative = Expr.relative[ tokens[0].type ],
+		implicitRelative = leadingRelative || Expr.relative[" "],
+		i = leadingRelative ? 1 : 0,
+
+		// The foundational matcher ensures that elements are reachable from top-level context(s)
+		matchContext = addCombinator( function( elem ) {
+			return elem === checkContext;
+		}, implicitRelative, true ),
+		matchAnyContext = addCombinator( function( elem ) {
+			return indexOf.call( checkContext, elem ) > -1;
+		}, implicitRelative, true ),
+		matchers = [ function( elem, context, xml ) {
+			return ( !leadingRelative && ( xml || context !== outermostContext ) ) || (
+				(checkContext = context).nodeType ?
+					matchContext( elem, context, xml ) :
+					matchAnyContext( elem, context, xml ) );
+		} ];
+
+	for ( ; i < len; i++ ) {
+		if ( (matcher = Expr.relative[ tokens[i].type ]) ) {
+			matchers = [ addCombinator(elementMatcher( matchers ), matcher) ];
+		} else {
+			matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches );
+
+			// Return special upon seeing a positional matcher
+			if ( matcher[ expando ] ) {
+				// Find the next relative operator (if any) for proper handling
+				j = ++i;
+				for ( ; j < len; j++ ) {
+					if ( Expr.relative[ tokens[j].type ] ) {
+						break;
+					}
+				}
+				return setMatcher(
+					i > 1 && elementMatcher( matchers ),
+					i > 1 && toSelector(
+						// If the preceding token was a descendant combinator, insert an implicit any-element `*`
+						tokens.slice( 0, i - 1 ).concat({ value: tokens[ i - 2 ].type === " " ? "*" : "" })
+					).replace( rtrim, "$1" ),
+					matcher,
+					i < j && matcherFromTokens( tokens.slice( i, j ) ),
+					j < len && matcherFromTokens( (tokens = tokens.slice( j )) ),
+					j < len && toSelector( tokens )
+				);
+			}
+			matchers.push( matcher );
+		}
+	}
+
+	return elementMatcher( matchers );
+}
+
+function matcherFromGroupMatchers( elementMatchers, setMatchers ) {
+	var bySet = setMatchers.length > 0,
+		byElement = elementMatchers.length > 0,
+		superMatcher = function( seed, context, xml, results, outermost ) {
+			var elem, j, matcher,
+				matchedCount = 0,
+				i = "0",
+				unmatched = seed && [],
+				setMatched = [],
+				contextBackup = outermostContext,
+				// We must always have either seed elements or outermost context
+				elems = seed || byElement && Expr.find["TAG"]( "*", outermost ),
+				// Use integer dirruns iff this is the outermost matcher
+				dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.random() || 0.1),
+				len = elems.length;
+
+			if ( outermost ) {
+				outermostContext = context !== document && context;
+			}
+
+			// Add elements passing elementMatchers directly to results
+			// Keep `i` a string if there are no elements so `matchedCount` will be "00" below
+			// Support: IE<9, Safari
+			// Tolerate NodeList properties (IE: "length"; Safari: <number>) matching elements by id
+			for ( ; i !== len && (elem = elems[i]) != null; i++ ) {
+				if ( byElement && elem ) {
+					j = 0;
+					while ( (matcher = elementMatchers[j++]) ) {
+						if ( matcher( elem, context, xml ) ) {
+							results.push( elem );
+							break;
+						}
+					}
+					if ( outermost ) {
+						dirruns = dirrunsUnique;
+					}
+				}
+
+				// Track unmatched elements for set filters
+				if ( bySet ) {
+					// They will have gone through all possible matchers
+					if ( (elem = !matcher && elem) ) {
+						matchedCount--;
+					}
+
+					// Lengthen the array for every element, matched or not
+					if ( seed ) {
+						unmatched.push( elem );
+					}
+				}
+			}
+
+			// Apply set filters to unmatched elements
+			matchedCount += i;
+			if ( bySet && i !== matchedCount ) {
+				j = 0;
+				while ( (matcher = setMatchers[j++]) ) {
+					matcher( unmatched, setMatched, context, xml );
+				}
+
+				if ( seed ) {
+					// Reintegrate element matches to eliminate the need for sorting
+					if ( matchedCount > 0 ) {
+						while ( i-- ) {
+							if ( !(unmatched[i] || setMatched[i]) ) {
+								setMatched[i] = pop.call( results );
+							}
+						}
+					}
+
+					// Discard index placeholder values to get only actual matches
+					setMatched = condense( setMatched );
+				}
+
+				// Add matches to results
+				push.apply( results, setMatched );
+
+				// Seedless set matches succeeding multiple successful matchers stipulate sorting
+				if ( outermost && !seed && setMatched.length > 0 &&
+					( matchedCount + setMatchers.length ) > 1 ) {
+
+					Sizzle.uniqueSort( results );
+				}
+			}
+
+			// Override manipulation of globals by nested matchers
+			if ( outermost ) {
+				dirruns = dirrunsUnique;
+				outermostContext = contextBackup;
+			}
+
+			return unmatched;
+		};
+
+	return bySet ?
+		markFunction( superMatcher ) :
+		superMatcher;
+}
+
+compile = Sizzle.compile = function( selector, match /* Internal Use Only */ ) {
+	var i,
+		setMatchers = [],
+		elementMatchers = [],
+		cached = compilerCache[ selector + " " ];
+
+	if ( !cached ) {
+		// Generate a function of recursive functions that can be used to check each element
+		if ( !match ) {
+			match = tokenize( selector );
+		}
+		i = match.length;
+		while ( i-- ) {
+			cached = matcherFromTokens( match[i] );
+			if ( cached[ expando ] ) {
+				setMatchers.push( cached );
+			} else {
+				elementMatchers.push( cached );
+			}
+		}
+
+		// Cache the compiled function
+		cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) );
+
+		// Save selector and tokenization
+		cached.selector = selector;
+	}
+	return cached;
+};
+
+/**
+ * A low-level selection function that works with Sizzle's compiled
+ *  selector functions
+ * @param {String|Function} selector A selector or a pre-compiled
+ *  selector function built with Sizzle.compile
+ * @param {Element} context
+ * @param {Array} [results]
+ * @param {Array} [seed] A set of elements to match against
+ */
+select = Sizzle.select = function( selector, context, results, seed ) {
+	var i, tokens, token, type, find,
+		compiled = typeof selector === "function" && selector,
+		match = !seed && tokenize( (selector = compiled.selector || selector) );
+
+	results = results || [];
+
+	// Try to minimize operations if there is no seed and only one group
+	if ( match.length === 1 ) {
+
+		// Take a shortcut and set the context if the root selector is an ID
+		tokens = match[0] = match[0].slice( 0 );
+		if ( tokens.length > 2 && (token = tokens[0]).type === "ID" &&
+				support.getById && context.nodeType === 9 && documentIsHTML &&
+				Expr.relative[ tokens[1].type ] ) {
+
+			context = ( Expr.find["ID"]( token.matches[0].replace(runescape, funescape), context ) || [] )[0];
+			if ( !context ) {
+				return results;
+
+			// Precompiled matchers will still verify ancestry, so step up a level
+			} else if ( compiled ) {
+				context = context.parentNode;
+			}
+
+			selector = selector.slice( tokens.shift().value.length );
+		}
+
+		// Fetch a seed set for right-to-left matching
+		i = matchExpr["needsContext"].test( selector ) ? 0 : tokens.length;
+		while ( i-- ) {
+			token = tokens[i];
+
+			// Abort if we hit a combinator
+			if ( Expr.relative[ (type = token.type) ] ) {
+				break;
+			}
+			if ( (find = Expr.find[ type ]) ) {
+				// Search, expanding context for leading sibling combinators
+				if ( (seed = find(
+					token.matches[0].replace( runescape, funescape ),
+					rsibling.test( tokens[0].type ) && testContext( context.parentNode ) || context
+				)) ) {
+
+					// If seed is empty or no tokens remain, we can return early
+					tokens.splice( i, 1 );
+					selector = seed.length && toSelector( tokens );
+					if ( !selector ) {
+						push.apply( results, seed );
+						return results;
+					}
+
+					break;
+				}
+			}
+		}
+	}
+
+	// Compile and execute a filtering function if one is not provided
+	// Provide `match` to avoid retokenization if we modified the selector above
+	( compiled || compile( selector, match ) )(
+		seed,
+		context,
+		!documentIsHTML,
+		results,
+		rsibling.test( selector ) && testContext( context.parentNode ) || context
+	);
+	return results;
+};
+
+// One-time assignments
+
+// Sort stability
+support.sortStable = expando.split("").sort( sortOrder ).join("") === expando;
+
+// Support: Chrome<14
+// Always assume duplicates if they aren't passed to the comparison function
+support.detectDuplicates = !!hasDuplicate;
+
+// Initialize against the default document
+setDocument();
+
+// Support: Webkit<537.32 - Safari 6.0.3/Chrome 25 (fixed in Chrome 27)
+// Detached nodes confoundingly follow *each other*
+support.sortDetached = assert(function( div1 ) {
+	// Should return 1, but returns 4 (following)
+	return div1.compareDocumentPosition( document.createElement("div") ) & 1;
+});
+
+// Support: IE<8
+// Prevent attribute/property "interpolation"
+// http://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx
+if ( !assert(function( div ) {
+	div.innerHTML = "<a href='#'></a>";
+	return div.firstChild.getAttribute("href") === "#" ;
+}) ) {
+	addHandle( "type|href|height|width", function( elem, name, isXML ) {
+		if ( !isXML ) {
+			return elem.getAttribute( name, name.toLowerCase() === "type" ? 1 : 2 );
+		}
+	});
+}
+
+// Support: IE<9
+// Use defaultValue in place of getAttribute("value")
+if ( !support.attributes || !assert(function( div ) {
+	div.innerHTML = "<input/>";
+	div.firstChild.setAttribute( "value", "" );
+	return div.firstChild.getAttribute( "value" ) === "";
+}) ) {
+	addHandle( "value", function( elem, name, isXML ) {
+		if ( !isXML && elem.nodeName.toLowerCase() === "input" ) {
+			return elem.defaultValue;
+		}
+	});
+}
+
+// Support: IE<9
+// Use getAttributeNode to fetch booleans when getAttribute lies
+if ( !assert(function( div ) {
+	return div.getAttribute("disabled") == null;
+}) ) {
+	addHandle( booleans, function( elem, name, isXML ) {
+		var val;
+		if ( !isXML ) {
+			return elem[ name ] === true ? name.toLowerCase() :
+					(val = elem.getAttributeNode( name )) && val.specified ?
+					val.value :
+				null;
+		}
+	});
+}
+
+return Sizzle;
+
+})( window );
+
+
+
+jQuery.find = Sizzle;
+jQuery.expr = Sizzle.selectors;
+jQuery.expr[":"] = jQuery.expr.pseudos;
+jQuery.unique = Sizzle.uniqueSort;
+jQuery.text = Sizzle.getText;
+jQuery.isXMLDoc = Sizzle.isXML;
+jQuery.contains = Sizzle.contains;
+
+
+
+var rneedsContext = jQuery.expr.match.needsContext;
+
+var rsingleTag = (/^<(\w+)\s*\/?>(?:<\/\1>|)$/);
+
+
+
+var risSimple = /^.[^:#\[\.,]*$/;
+
+// Implement the identical functionality for filter and not
+function winnow( elements, qualifier, not ) {
+	if ( jQuery.isFunction( qualifier ) ) {
+		return jQuery.grep( elements, function( elem, i ) {
+			/* jshint -W018 */
+			return !!qualifier.call( elem, i, elem ) !== not;
+		});
+
+	}
+
+	if ( qualifier.nodeType ) {
+		return jQuery.grep( elements, function( elem ) {
+			return ( elem === qualifier ) !== not;
+		});
+
+	}
+
+	if ( typeof qualifier === "string" ) {
+		if ( risSimple.test( qualifier ) ) {
+			return jQuery.filter( qualifier, elements, not );
+		}
+
+		qualifier = jQuery.filter( qualifier, elements );
+	}
+
+	return jQuery.grep( elements, function( elem ) {
+		return ( indexOf.call( qualifier, elem ) >= 0 ) !== not;
+	});
+}
+
+jQuery.filter = function( expr, elems, not ) {
+	var elem = elems[ 0 ];
+
+	if ( not ) {
+		expr = ":not(" + expr + ")";
+	}
+
+	return elems.length === 1 && elem.nodeType === 1 ?
+		jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : [] :
+		jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) {
+			return elem.nodeType === 1;
+		}));
+};
+
+jQuery.fn.extend({
+	find: function( selector ) {
+		var i,
+			len = this.length,
+			ret = [],
+			self = this;
+
+		if ( typeof selector !== "string" ) {
+			return this.pushStack( jQuery( selector ).filter(function() {
+				for ( i = 0; i < len; i++ ) {
+					if ( jQuery.contains( self[ i ], this ) ) {
+						return true;
+					}
+				}
+			}) );
+		}
+
+		for ( i = 0; i < len; i++ ) {
+			jQuery.find( selector, self[ i ], ret );
+		}
+
+		// Needed because $( selector, context ) becomes $( context ).find( selector )
+		ret = this.pushStack( len > 1 ? jQuery.unique( ret ) : ret );
+		ret.selector = this.selector ? this.selector + " " + selector : selector;
+		return ret;
+	},
+	filter: function( selector ) {
+		return this.pushStack( winnow(this, selector || [], false) );
+	},
+	not: function( selector ) {
+		return this.pushStack( winnow(this, selector || [], true) );
+	},
+	is: function( selector ) {
+		return !!winnow(
+			this,
+
+			// If this is a positional/relative selector, check membership in the returned set
+			// so $("p:first").is("p:last") won't return true for a doc with two "p".
+			typeof selector === "string" && rneedsContext.test( selector ) ?
+				jQuery( selector ) :
+				selector || [],
+			false
+		).length;
+	}
+});
+
+
+// Initialize a jQuery object
+
+
+// A central reference to the root jQuery(document)
+var rootjQuery,
+
+	// A simple way to check for HTML strings
+	// Prioritize #id over <tag> to avoid XSS via location.hash (#9521)
+	// Strict HTML recognition (#11290: must start with <)
+	rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,
+
+	init = jQuery.fn.init = function( selector, context ) {
+		var match, elem;
+
+		// HANDLE: $(""), $(null), $(undefined), $(false)
+		if ( !selector ) {
+			return this;
+		}
+
+		// Handle HTML strings
+		if ( typeof selector === "string" ) {
+			if ( selector[0] === "<" && selector[ selector.length - 1 ] === ">" && selector.length >= 3 ) {
+				// Assume that strings that start and end with <> are HTML and skip the regex check
+				match = [ null, selector, null ];
+
+			} else {
+				match = rquickExpr.exec( selector );
+			}
+
+			// Match html or make sure no context is specified for #id
+			if ( match && (match[1] || !context) ) {
+
+				// HANDLE: $(html) -> $(array)
+				if ( match[1] ) {
+					context = context instanceof jQuery ? context[0] : context;
+
+					// scripts is true for back-compat
+					// Intentionally let the error be thrown if parseHTML is not present
+					jQuery.merge( this, jQuery.parseHTML(
+						match[1],
+						context && context.nodeType ? context.ownerDocument || context : document,
+						true
+					) );
+
+					// HANDLE: $(html, props)
+					if ( rsingleTag.test( match[1] ) && jQuery.isPlainObject( context ) ) {
+						for ( match in context ) {
+							// Properties of context are called as methods if possible
+							if ( jQuery.isFunction( this[ match ] ) ) {
+								this[ match ]( context[ match ] );
+
+							// ...and otherwise set as attributes
+							} else {
+								this.attr( match, context[ match ] );
+							}
+						}
+					}
+
+					return this;
+
+				// HANDLE: $(#id)
+				} else {
+					elem = document.getElementById( match[2] );
+
+					// Check parentNode to catch when Blackberry 4.6 returns
+					// nodes that are no longer in the document #6963
+					if ( elem && elem.parentNode ) {
+						// Inject the element directly into the jQuery object
+						this.length = 1;
+						this[0] = elem;
+					}
+
+					this.context = document;
+					this.selector = selector;
+					return this;
+				}
+
+			// HANDLE: $(expr, $(...))
+			} else if ( !context || context.jquery ) {
+				return ( context || rootjQuery ).find( selector );
+
+			// HANDLE: $(expr, context)
+			// (which is just equivalent to: $(context).find(expr)
+			} else {
+				return this.constructor( context ).find( selector );
+			}
+
+		// HANDLE: $(DOMElement)
+		} else if ( selector.nodeType ) {
+			this.context = this[0] = selector;
+			this.length = 1;
+			return this;
+
+		// HANDLE: $(function)
+		// Shortcut for document ready
+		} else if ( jQuery.isFunction( selector ) ) {
+			return typeof rootjQuery.ready !== "undefined" ?
+				rootjQuery.ready( selector ) :
+				// Execute immediately if ready is not present
+				selector( jQuery );
+		}
+
+		if ( selector.selector !== undefined ) {
+			this.selector = selector.selector;
+			this.context = selector.context;
+		}
+
+		return jQuery.makeArray( selector, this );
+	};
+
+// Give the init function the jQuery prototype for later instantiation
+init.prototype = jQuery.fn;
+
+// Initialize central reference
+rootjQuery = jQuery( document );
+
+
+var rparentsprev = /^(?:parents|prev(?:Until|All))/,
+	// methods guaranteed to produce a unique set when starting from a unique set
+	guaranteedUnique = {
+		children: true,
+		contents: true,
+		next: true,
+		prev: true
+	};
+
+jQuery.extend({
+	dir: function( elem, dir, until ) {
+		var matched = [],
+			truncate = until !== undefined;
+
+		while ( (elem = elem[ dir ]) && elem.nodeType !== 9 ) {
+			if ( elem.nodeType === 1 ) {
+				if ( truncate && jQuery( elem ).is( until ) ) {
+					break;
+				}
+				matched.push( elem );
+			}
+		}
+		return matched;
+	},
+
+	sibling: function( n, elem ) {
+		var matched = [];
+
+		for ( ; n; n = n.nextSibling ) {
+			if ( n.nodeType === 1 && n !== elem ) {
+				matched.push( n );
+			}
+		}
+
+		return matched;
+	}
+});
+
+jQuery.fn.extend({
+	has: function( target ) {
+		var targets = jQuery( target, this ),
+			l = targets.length;
+
+		return this.filter(function() {
+			var i = 0;
+			for ( ; i < l; i++ ) {
+				if ( jQuery.contains( this, targets[i] ) ) {
+					return true;
+				}
+			}
+		});
+	},
+
+	closest: function( selectors, context ) {
+		var cur,
+			i = 0,
+			l = this.length,
+			matched = [],
+			pos = rneedsContext.test( selectors ) || typeof selectors !== "string" ?
+				jQuery( selectors, context || this.context ) :
+				0;
+
+		for ( ; i < l; i++ ) {
+			for ( cur = this[i]; cur && cur !== context; cur = cur.parentNode ) {
+				// Always skip document fragments
+				if ( cur.nodeType < 11 && (pos ?
+					pos.index(cur) > -1 :
+
+					// Don't pass non-elements to Sizzle
+					cur.nodeType === 1 &&
+						jQuery.find.matchesSelector(cur, selectors)) ) {
+
+					matched.push( cur );
+					break;
+				}
+			}
+		}
+
+		return this.pushStack( matched.length > 1 ? jQuery.unique( matched ) : matched );
+	},
+
+	// Determine the position of an element within
+	// the matched set of elements
+	index: function( elem ) {
+
+		// No argument, return index in parent
+		if ( !elem ) {
+			return ( this[ 0 ] && this[ 0 ].parentNode ) ? this.first().prevAll().length : -1;
+		}
+
+		// index in selector
+		if ( typeof elem === "string" ) {
+			return indexOf.call( jQuery( elem ), this[ 0 ] );
+		}
+
+		// Locate the position of the desired element
+		return indexOf.call( this,
+
+			// If it receives a jQuery object, the first element is used
+			elem.jquery ? elem[ 0 ] : elem
+		);
+	},
+
+	add: function( selector, context ) {
+		return this.pushStack(
+			jQuery.unique(
+				jQuery.merge( this.get(), jQuery( selector, context ) )
+			)
+		);
+	},
+
+	addBack: function( selector ) {
+		return this.add( selector == null ?
+			this.prevObject : this.prevObject.filter(selector)
+		);
+	}
+});
+
+function sibling( cur, dir ) {
+	while ( (cur = cur[dir]) && cur.nodeType !== 1 ) {}
+	return cur;
+}
+
+jQuery.each({
+	parent: function( elem ) {
+		var parent = elem.parentNode;
+		return parent && parent.nodeType !== 11 ? parent : null;
+	},
+	parents: function( elem ) {
+		return jQuery.dir( elem, "parentNode" );
+	},
+	parentsUntil: function( elem, i, until ) {
+		return jQuery.dir( elem, "parentNode", until );
+	},
+	next: function( elem ) {
+		return sibling( elem, "nextSibling" );
+	},
+	prev: function( elem ) {
+		return sibling( elem, "previousSibling" );
+	},
+	nextAll: function( elem ) {
+		return jQuery.dir( elem, "nextSibling" );
+	},
+	prevAll: function( elem ) {
+		return jQuery.dir( elem, "previousSibling" );
+	},
+	nextUntil: function( elem, i, until ) {
+		return jQuery.dir( elem, "nextSibling", until );
+	},
+	prevUntil: function( elem, i, until ) {
+		return jQuery.dir( elem, "previousSibling", until );
+	},
+	siblings: function( elem ) {
+		return jQuery.sibling( ( elem.parentNode || {} ).firstChild, elem );
+	},
+	children: function( elem ) {
+		return jQuery.sibling( elem.firstChild );
+	},
+	contents: function( elem ) {
+		return elem.contentDocument || jQuery.merge( [], elem.childNodes );
+	}
+}, function( name, fn ) {
+	jQuery.fn[ name ] = function( until, selector ) {
+		var matched = jQuery.map( this, fn, until );
+
+		if ( name.slice( -5 ) !== "Until" ) {
+			selector = until;
+		}
+
+		if ( selector && typeof selector === "string" ) {
+			matched = jQuery.filter( selector, matched );
+		}
+
+		if ( this.length > 1 ) {
+			// Remove duplicates
+			if ( !guaranteedUnique[ name ] ) {
+				jQuery.unique( matched );
+			}
+
+			// Reverse order for parents* and prev-derivatives
+			if ( rparentsprev.test( name ) ) {
+				matched.reverse();
+			}
+		}
+
+		return this.pushStack( matched );
+	};
+});
+var rnotwhite = (/\S+/g);
+
+
+
+// String to Object options format cache
+var optionsCache = {};
+
+// Convert String-formatted options into Object-formatted ones and store in cache
+function createOptions( options ) {
+	var object = optionsCache[ options ] = {};
+	jQuery.each( options.match( rnotwhite ) || [], function( _, flag ) {
+		object[ flag ] = true;
+	});
+	return object;
+}
+
+/*
+ * Create a callback list using the following parameters:
+ *
+ *	options: an optional list of space-separated options that will change how
+ *			the callback list behaves or a more traditional option object
+ *
+ * By default a callback list will act like an event callback list and can be
+ * "fired" multiple times.
+ *
+ * Possible options:
+ *
+ *	once:			will ensure the callback list can only be fired once (like a Deferred)
+ *
+ *	memory:			will keep track of previous values and will call any callback added
+ *					after the list has been fired right away with the latest "memorized"
+ *					values (like a Deferred)
+ *
+ *	unique:			will ensure a callback can only be added once (no duplicate in the list)
+ *
+ *	stopOnFalse:	interrupt callings when a callback returns false
+ *
+ */
+jQuery.Callbacks = function( options ) {
+
+	// Convert options from String-formatted to Object-formatted if needed
+	// (we check in cache first)
+	options = typeof options === "string" ?
+		( optionsCache[ options ] || createOptions( options ) ) :
+		jQuery.extend( {}, options );
+
+	var // Last fire value (for non-forgettable lists)
+		memory,
+		// Flag to know if list was already fired
+		fired,
+		// Flag to know if list is currently firing
+		firing,
+		// First callback to fire (used internally by add and fireWith)
+		firingStart,
+		// End of the loop when firing
+		firingLength,
+		// Index of currently firing callback (modified by remove if needed)
+		firingIndex,
+		// Actual callback list
+		list = [],
+		// Stack of fire calls for repeatable lists
+		stack = !options.once && [],
+		// Fire callbacks
+		fire = function( data ) {
+			memory = options.memory && data;
+			fired = true;
+			firingIndex = firingStart || 0;
+			firingStart = 0;
+			firingLength = list.length;
+			firing = true;
+			for ( ; list && firingIndex < firingLength; firingIndex++ ) {
+				if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) {
+					memory = false; // To prevent further calls using add
+					break;
+				}
+			}
+			firing = false;
+			if ( list ) {
+				if ( stack ) {
+					if ( stack.length ) {
+						fire( stack.shift() );
+					}
+				} else if ( memory ) {
+					list = [];
+				} else {
+					self.disable();
+				}
+			}
+		},
+		// Actual Callbacks object
+		self = {
+			// Add a callback or a collection of callbacks to the list
+			add: function() {
+				if ( list ) {
+					// First, we save the current length
+					var start = list.length;
+					(function add( args ) {
+						jQuery.each( args, function( _, arg ) {
+							var type = jQuery.type( arg );
+							if ( type === "function" ) {
+								if ( !options.unique || !self.has( arg ) ) {
+									list.push( arg );
+								}
+							} else if ( arg && arg.length && type !== "string" ) {
+								// Inspect recursively
+								add( arg );
+							}
+						});
+					})( arguments );
+					// Do we need to add the callbacks to the
+					// current firing batch?
+					if ( firing ) {
+						firingLength = list.length;
+					// With memory, if we're not firing then
+					// we should call right away
+					} else if ( memory ) {
+						firingStart = start;
+						fire( memory );
+					}
+				}
+				return this;
+			},
+			// Remove a callback from the list
+			remove: function() {
+				if ( list ) {
+					jQuery.each( arguments, function( _, arg ) {
+						var index;
+						while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) {
+							list.splice( index, 1 );
+							// Handle firing indexes
+							if ( firing ) {
+								if ( index <= firingLength ) {
+									firingLength--;
+								}
+								if ( index <= firingIndex ) {
+									firingIndex--;
+								}
+							}
+						}
+					});
+				}
+				return this;
+			},
+			// Check if a given callback is in the list.
+			// If no argument is given, return whether or not list has callbacks attached.
+			has: function( fn ) {
+				return fn ? jQuery.inArray( fn, list ) > -1 : !!( list && list.length );
+			},
+			// Remove all callbacks from the list
+			empty: function() {
+				list = [];
+				firingLength = 0;
+				return this;
+			},
+			// Have the list do nothing anymore
+			disable: function() {
+				list = stack = memory = undefined;
+				return this;
+			},
+			// Is it disabled?
+			disabled: function() {
+				return !list;
+			},
+			// Lock the list in its current state
+			lock: function() {
+				stack = undefined;
+				if ( !memory ) {
+					self.disable();
+				}
+				return this;
+			},
+			// Is it locked?
+			locked: function() {
+				return !stack;
+			},
+			// Call all callbacks with the given context and arguments
+			fireWith: function( context, args ) {
+				if ( list && ( !fired || stack ) ) {
+					args = args || [];
+					args = [ context, args.slice ? args.slice() : args ];
+					if ( firing ) {
+						stack.push( args );
+					} else {
+						fire( args );
+					}
+				}
+				return this;
+			},
+			// Call all the callbacks with the given arguments
+			fire: function() {
+				self.fireWith( this, arguments );
+				return this;
+			},
+			// To know if the callbacks have already been called at least once
+			fired: function() {
+				return !!fired;
+			}
+		};
+
+	return self;
+};
+
+
+jQuery.extend({
+
+	Deferred: function( func ) {
+		var tuples = [
+				// action, add listener, listener list, final state
+				[ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ],
+				[ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ],
+				[ "notify", "progress", jQuery.Callbacks("memory") ]
+			],
+			state = "pending",
+			promise = {
+				state: function() {
+					return state;
+				},
+				always: function() {
+					deferred.done( arguments ).fail( arguments );
+					return this;
+				},
+				then: function( /* fnDone, fnFail, fnProgress */ ) {
+					var fns = arguments;
+					return jQuery.Deferred(function( newDefer ) {
+						jQuery.each( tuples, function( i, tuple ) {
+							var fn = jQuery.isFunction( fns[ i ] ) && fns[ i ];
+							// deferred[ done | fail | progress ] for forwarding actions to newDefer
+							deferred[ tuple[1] ](function() {
+								var returned = fn && fn.apply( this, arguments );
+								if ( returned && jQuery.isFunction( returned.promise ) ) {
+									returned.promise()
+										.done( newDefer.resolve )
+										.fail( newDefer.reject )
+										.progress( newDefer.notify );
+								} else {
+									newDefer[ tuple[ 0 ] + "With" ]( this === promise ? newDefer.promise() : this, fn ? [ returned ] : arguments );
+								}
+							});
+						});
+						fns = null;
+					}).promise();
+				},
+				// Get a promise for this deferred
+				// If obj is provided, the promise aspect is added to the object
+				promise: function( obj ) {
+					return obj != null ? jQuery.extend( obj, promise ) : promise;
+				}
+			},
+			deferred = {};
+
+		// Keep pipe for back-compat
+		promise.pipe = promise.then;
+
+		// Add list-specific methods
+		jQuery.each( tuples, function( i, tuple ) {
+			var list = tuple[ 2 ],
+				stateString = tuple[ 3 ];
+
+			// promise[ done | fail | progress ] = list.add
+			promise[ tuple[1] ] = list.add;
+
+			// Handle state
+			if ( stateString ) {
+				list.add(function() {
+					// state = [ resolved | rejected ]
+					state = stateString;
+
+				// [ reject_list | resolve_list ].disable; progress_list.lock
+				}, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock );
+			}
+
+			// deferred[ resolve | reject | notify ]
+			deferred[ tuple[0] ] = function() {
+				deferred[ tuple[0] + "With" ]( this === deferred ? promise : this, arguments );
+				return this;
+			};
+			deferred[ tuple[0] + "With" ] = list.fireWith;
+		});
+
+		// Make the deferred a promise
+		promise.promise( deferred );
+
+		// Call given func if any
+		if ( func ) {
+			func.call( deferred, deferred );
+		}
+
+		// All done!
+		return deferred;
+	},
+
+	// Deferred helper
+	when: function( subordinate /* , ..., subordinateN */ ) {
+		var i = 0,
+			resolveValues = slice.call( arguments ),
+			length = resolveValues.length,
+
+			// the count of uncompleted subordinates
+			remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0,
+
+			// the master Deferred. If resolveValues consist of only a single Deferred, just use that.
+			deferred = remaining === 1 ? subordinate : jQuery.Deferred(),
+
+			// Update function for both resolve and progress values
+			updateFunc = function( i, contexts, values ) {
+				return function( value ) {
+					contexts[ i ] = this;
+					values[ i ] = arguments.length > 1 ? slice.call( arguments ) : value;
+					if ( values === progressValues ) {
+						deferred.notifyWith( contexts, values );
+					} else if ( !( --remaining ) ) {
+						deferred.resolveWith( contexts, values );
+					}
+				};
+			},
+
+			progressValues, progressContexts, resolveContexts;
+
+		// add listeners to Deferred subordinates; treat others as resolved
+		if ( length > 1 ) {
+			progressValues = new Array( length );
+			progressContexts = new Array( length );
+			resolveContexts = new Array( length );
+			for ( ; i < length; i++ ) {
+				if ( resolveValues[ i ] && jQuery.isFunction( resolveValues[ i ].promise ) ) {
+					resolveValues[ i ].promise()
+						.done( updateFunc( i, resolveContexts, resolveValues ) )
+						.fail( deferred.reject )
+						.progress( updateFunc( i, progressContexts, progressValues ) );
+				} else {
+					--remaining;
+				}
+			}
+		}
+
+		// if we're not waiting on anything, resolve the master
+		if ( !remaining ) {
+			deferred.resolveWith( resolveContexts, resolveValues );
+		}
+
+		return deferred.promise();
+	}
+});
+
+
+// The deferred used on DOM ready
+var readyList;
+
+jQuery.fn.ready = function( fn ) {
+	// Add the callback
+	jQuery.ready.promise().done( fn );
+
+	return this;
+};
+
+jQuery.extend({
+	// Is the DOM ready to be used? Set to true once it occurs.
+	isReady: false,
+
+	// A counter to track how many items to wait for before
+	// the ready event fires. See #6781
+	readyWait: 1,
+
+	// Hold (or release) the ready event
+	holdReady: function( hold ) {
+		if ( hold ) {
+			jQuery.readyWait++;
+		} else {
+			jQuery.ready( true );
+		}
+	},
+
+	// Handle when the DOM is ready
+	ready: function( wait ) {
+
+		// Abort if there are pending holds or we're already ready
+		if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) {
+			return;
+		}
+
+		// Remember that the DOM is ready
+		jQuery.isReady = true;
+
+		// If a normal DOM Ready event fired, decrement, and wait if need be
+		if ( wait !== true && --jQuery.readyWait > 0 ) {
+			return;
+		}
+
+		// If there are functions bound, to execute
+		readyList.resolveWith( document, [ jQuery ] );
+
+		// Trigger any bound ready events
+		if ( jQuery.fn.triggerHandler ) {
+			jQuery( document ).triggerHandler( "ready" );
+			jQuery( document ).off( "ready" );
+		}
+	}
+});
+
+/**
+ * The ready event handler and self cleanup method
+ */
+function completed() {
+	document.removeEventListener( "DOMContentLoaded", completed, false );
+	window.removeEventListener( "load", completed, false );
+	jQuery.ready();
+}
+
+jQuery.ready.promise = function( obj ) {
+	if ( !readyList ) {
+
+		readyList = jQuery.Deferred();
+
+		// Catch cases where $(document).ready() is called after the browser event has already occurred.
+		// we once tried to use readyState "interactive" here, but it caused issues like the one
+		// discovered by ChrisS here: http://bugs.jquery.com/ticket/12282#comment:15
+		if ( document.readyState === "complete" ) {
+			// Handle it asynchronously to allow scripts the opportunity to delay ready
+			setTimeout( jQuery.ready );
+
+		} else {
+
+			// Use the handy event callback
+			document.addEventListener( "DOMContentLoaded", completed, false );
+
+			// A fallback to window.onload, that will always work
+			window.addEventListener( "load", completed, false );
+		}
+	}
+	return readyList.promise( obj );
+};
+
+// Kick off the DOM ready check even if the user does not
+jQuery.ready.promise();
+
+
+
+
+// Multifunctional method to get and set values of a collection
+// The value/s can optionally be executed if it's a function
+var access = jQuery.access = function( elems, fn, key, value, chainable, emptyGet, raw ) {
+	var i = 0,
+		len = elems.length,
+		bulk = key == null;
+
+	// Sets many values
+	if ( jQuery.type( key ) === "object" ) {
+		chainable = true;
+		for ( i in key ) {
+			jQuery.access( elems, fn, i, key[i], true, emptyGet, raw );
+		}
+
+	// Sets one value
+	} else if ( value !== undefined ) {
+		chainable = true;
+
+		if ( !jQuery.isFunction( value ) ) {
+			raw = true;
+		}
+
+		if ( bulk ) {
+			// Bulk operations run against the entire set
+			if ( raw ) {
+				fn.call( elems, value );
+				fn = null;
+
+			// ...except when executing function values
+			} else {
+				bulk = fn;
+				fn = function( elem, key, value ) {
+					return bulk.call( jQuery( elem ), value );
+				};
+			}
+		}
+
+		if ( fn ) {
+			for ( ; i < len; i++ ) {
+				fn( elems[i], key, raw ? value : value.call( elems[i], i, fn( elems[i], key ) ) );
+			}
+		}
+	}
+
+	return chainable ?
+		elems :
+
+		// Gets
+		bulk ?
+			fn.call( elems ) :
+			len ? fn( elems[0], key ) : emptyGet;
+};
+
+
+/**
+ * Determines whether an object can have data
+ */
+jQuery.acceptData = function( owner ) {
+	// Accepts only:
+	//  - Node
+	//    - Node.ELEMENT_NODE
+	//    - Node.DOCUMENT_NODE
+	//  - Object
+	//    - Any
+	/* jshint -W018 */
+	return owner.nodeType === 1 || owner.nodeType === 9 || !( +owner.nodeType );
+};
+
+
+function Data() {
+	// Support: Android < 4,
+	// Old WebKit does not have Object.preventExtensions/freeze method,
+	// return new empty object instead with no [[set]] accessor
+	Object.defineProperty( this.cache = {}, 0, {
+		get: function() {
+			return {};
+		}
+	});
+
+	this.expando = jQuery.expando + Math.random();
+}
+
+Data.uid = 1;
+Data.accepts = jQuery.acceptData;
+
+Data.prototype = {
+	key: function( owner ) {
+		// We can accept data for non-element nodes in modern browsers,
+		// but we should not, see #8335.
+		// Always return the key for a frozen object.
+		if ( !Data.accepts( owner ) ) {
+			return 0;
+		}
+
+		var descriptor = {},
+			// Check if the owner object already has a cache key
+			unlock = owner[ this.expando ];
+
+		// If not, create one
+		if ( !unlock ) {
+			unlock = Data.uid++;
+
+			// Secure it in a non-enumerable, non-writable property
+			try {
+				descriptor[ this.expando ] = { value: unlock };
+				Object.defineProperties( owner, descriptor );
+
+			// Support: Android < 4
+			// Fallback to a less secure definition
+			} catch ( e ) {
+				descriptor[ this.expando ] = unlock;
+				jQuery.extend( owner, descriptor );
+			}
+		}
+
+		// Ensure the cache object
+		if ( !this.cache[ unlock ] ) {
+			this.cache[ unlock ] = {};
+		}
+
+		return unlock;
+	},
+	set: function( owner, data, value ) {
+		var prop,
+			// There may be an unlock assigned to this node,
+			// if there is no entry for this "owner", create one inline
+			// and set the unlock as though an owner entry had always existed
+			unlock = this.key( owner ),
+			cache = this.cache[ unlock ];
+
+		// Handle: [ owner, key, value ] args
+		if ( typeof data === "string" ) {
+			cache[ data ] = value;
+
+		// Handle: [ owner, { properties } ] args
+		} else {
+			// Fresh assignments by object are shallow copied
+			if ( jQuery.isEmptyObject( cache ) ) {
+				jQuery.extend( this.cache[ unlock ], data );
+			// Otherwise, copy the properties one-by-one to the cache object
+			} else {
+				for ( prop in data ) {
+					cache[ prop ] = data[ prop ];
+				}
+			}
+		}
+		return cache;
+	},
+	get: function( owner, key ) {
+		// Either a valid cache is found, or will be created.
+		// New caches will be created and the unlock returned,
+		// allowing direct access to the newly created
+		// empty data object. A valid owner object must be provided.
+		var cache = this.cache[ this.key( owner ) ];
+
+		return key === undefined ?
+			cache : cache[ key ];
+	},
+	access: function( owner, key, value ) {
+		var stored;
+		// In cases where either:
+		//
+		//   1. No key was specified
+		//   2. A string key was specified, but no value provided
+		//
+		// Take the "read" path and allow the get method to determine
+		// which value to return, respectively either:
+		//
+		//   1. The entire cache object
+		//   2. The data stored at the key
+		//
+		if ( key === undefined ||
+				((key && typeof key === "string") && value === undefined) ) {
+
+			stored = this.get( owner, key );
+
+			return stored !== undefined ?
+				stored : this.get( owner, jQuery.camelCase(key) );
+		}
+
+		// [*]When the key is not a string, or both a key and value
+		// are specified, set or extend (existing objects) with either:
+		//
+		//   1. An object of properties
+		//   2. A key and value
+		//
+		this.set( owner, key, value );
+
+		// Since the "set" path can have two possible entry points
+		// return the expected data based on which path was taken[*]
+		return value !== undefined ? value : key;
+	},
+	remove: function( owner, key ) {
+		var i, name, camel,
+			unlock = this.key( owner ),
+			cache = this.cache[ unlock ];
+
+		if ( key === undefined ) {
+			this.cache[ unlock ] = {};
+
+		} else {
+			// Support array or space separated string of keys
+			if ( jQuery.isArray( key ) ) {
+				// If "name" is an array of keys...
+				// When data is initially created, via ("key", "val") signature,
+				// keys will be converted to camelCase.
+				// Since there is no way to tell _how_ a key was added, remove
+				// both plain key and camelCase key. #12786
+				// This will only penalize the array argument path.
+				name = key.concat( key.map( jQuery.camelCase ) );
+			} else {
+				camel = jQuery.camelCase( key );
+				// Try the string as a key before any manipulation
+				if ( key in cache ) {
+					name = [ key, camel ];
+				} else {
+					// If a key with the spaces exists, use it.
+					// Otherwise, create an array by matching non-whitespace
+					name = camel;
+					name = name in cache ?
+						[ name ] : ( name.match( rnotwhite ) || [] );
+				}
+			}
+
+			i = name.length;
+			while ( i-- ) {
+				delete cache[ name[ i ] ];
+			}
+		}
+	},
+	hasData: function( owner ) {
+		return !jQuery.isEmptyObject(
+			this.cache[ owner[ this.expando ] ] || {}
+		);
+	},
+	discard: function( owner ) {
+		if ( owner[ this.expando ] ) {
+			delete this.cache[ owner[ this.expando ] ];
+		}
+	}
+};
+var data_priv = new Data();
+
+var data_user = new Data();
+
+
+
+/*
+	Implementation Summary
+
+	1. Enforce API surface and semantic compatibility with 1.9.x branch
+	2. Improve the module's maintainability by reducing the storage
+		paths to a single mechanism.
+	3. Use the same single mechanism to support "private" and "user" data.
+	4. _Never_ expose "private" data to user code (TODO: Drop _data, _removeData)
+	5. Avoid exposing implementation details on user objects (eg. expando properties)
+	6. Provide a clear path for implementation upgrade to WeakMap in 2014
+*/
+var rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,
+	rmultiDash = /([A-Z])/g;
+
+function dataAttr( elem, key, data ) {
+	var name;
+
+	// If nothing was found internally, try to fetch any
+	// data from the HTML5 data-* attribute
+	if ( data === undefined && elem.nodeType === 1 ) {
+		name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase();
+		data = elem.getAttribute( name );
+
+		if ( typeof data === "string" ) {
+			try {
+				data = data === "true" ? true :
+					data === "false" ? false :
+					data === "null" ? null :
+					// Only convert to a number if it doesn't change the string
+					+data + "" === data ? +data :
+					rbrace.test( data ) ? jQuery.parseJSON( data ) :
+					data;
+			} catch( e ) {}
+
+			// Make sure we set the data so it isn't changed later
+			data_user.set( elem, key, data );
+		} else {
+			data = undefined;
+		}
+	}
+	return data;
+}
+
+jQuery.extend({
+	hasData: function( elem ) {
+		return data_user.hasData( elem ) || data_priv.hasData( elem );
+	},
+
+	data: function( elem, name, data ) {
+		return data_user.access( elem, name, data );
+	},
+
+	removeData: function( elem, name ) {
+		data_user.remove( elem, name );
+	},
+
+	// TODO: Now that all calls to _data and _removeData have been replaced
+	// with direct calls to data_priv methods, these can be deprecated.
+	_data: function( elem, name, data ) {
+		return data_priv.access( elem, name, data );
+	},
+
+	_removeData: function( elem, name ) {
+		data_priv.remove( elem, name );
+	}
+});
+
+jQuery.fn.extend({
+	data: function( key, value ) {
+		var i, name, data,
+			elem = this[ 0 ],
+			attrs = elem && elem.attributes;
+
+		// Gets all values
+		if ( key === undefined ) {
+			if ( this.length ) {
+				data = data_user.get( elem );
+
+				if ( elem.nodeType === 1 && !data_priv.get( elem, "hasDataAttrs" ) ) {
+					i = attrs.length;
+					while ( i-- ) {
+
+						// Support: IE11+
+						// The attrs elements can be null (#14894)
+						if ( attrs[ i ] ) {
+							name = attrs[ i ].name;
+							if ( name.indexOf( "data-" ) === 0 ) {
+								name = jQuery.camelCase( name.slice(5) );
+								dataAttr( elem, name, data[ name ] );
+							}
+						}
+					}
+					data_priv.set( elem, "hasDataAttrs", true );
+				}
+			}
+
+			return data;
+		}
+
+		// Sets multiple values
+		if ( typeof key === "object" ) {
+			return this.each(function() {
+				data_user.set( this, key );
+			});
+		}
+
+		return access( this, function( value ) {
+			var data,
+				camelKey = jQuery.camelCase( key );
+
+			// The calling jQuery object (element matches) is not empty
+			// (and therefore has an element appears at this[ 0 ]) and the
+			// `value` parameter was not undefined. An empty jQuery object
+			// will result in `undefined` for elem = this[ 0 ] which will
+			// throw an exception if an attempt to read a data cache is made.
+			if ( elem && value === undefined ) {
+				// Attempt to get data from the cache
+				// with the key as-is
+				data = data_user.get( elem, key );
+				if ( data !== undefined ) {
+					return data;
+				}
+
+				// Attempt to get data from the cache
+				// with the key camelized
+				data = data_user.get( elem, camelKey );
+				if ( data !== undefined ) {
+					return data;
+				}
+
+				// Attempt to "discover" the data in
+				// HTML5 custom data-* attrs
+				data = dataAttr( elem, camelKey, undefined );
+				if ( data !== undefined ) {
+					return data;
+				}
+
+				// We tried really hard, but the data doesn't exist.
+				return;
+			}
+
+			// Set the data...
+			this.each(function() {
+				// First, attempt to store a copy or reference of any
+				// data that might've been store with a camelCased key.
+				var data = data_user.get( this, camelKey );
+
+				// For HTML5 data-* attribute interop, we have to
+				// store property names with dashes in a camelCase form.
+				// This might not apply to all properties...*
+				data_user.set( this, camelKey, value );
+
+				// *... In the case of properties that might _actually_
+				// have dashes, we need to also store a copy of that
+				// unchanged property.
+				if ( key.indexOf("-") !== -1 && data !== undefined ) {
+					data_user.set( this, key, value );
+				}
+			});
+		}, null, value, arguments.length > 1, null, true );
+	},
+
+	removeData: function( key ) {
+		return this.each(function() {
+			data_user.remove( this, key );
+		});
+	}
+});
+
+
+jQuery.extend({
+	queue: function( elem, type, data ) {
+		var queue;
+
+		if ( elem ) {
+			type = ( type || "fx" ) + "queue";
+			queue = data_priv.get( elem, type );
+
+			// Speed up dequeue by getting out quickly if this is just a lookup
+			if ( data ) {
+				if ( !queue || jQuery.isArray( data ) ) {
+					queue = data_priv.access( elem, type, jQuery.makeArray(data) );
+				} else {
+					queue.push( data );
+				}
+			}
+			return queue || [];
+		}
+	},
+
+	dequeue: function( elem, type ) {
+		type = type || "fx";
+
+		var queue = jQuery.queue( elem, type ),
+			startLength = queue.length,
+			fn = queue.shift(),
+			hooks = jQuery._queueHooks( elem, type ),
+			next = function() {
+				jQuery.dequeue( elem, type );
+			};
+
+		// If the fx queue is dequeued, always remove the progress sentinel
+		if ( fn === "inprogress" ) {
+			fn = queue.shift();
+			startLength--;
+		}
+
+		if ( fn ) {
+
+			// Add a progress sentinel to prevent the fx queue from being
+			// automatically dequeued
+			if ( type === "fx" ) {
+				queue.unshift( "inprogress" );
+			}
+
+			// clear up the last queue stop function
+			delete hooks.stop;
+			fn.call( elem, next, hooks );
+		}
+
+		if ( !startLength && hooks ) {
+			hooks.empty.fire();
+		}
+	},
+
+	// not intended for public consumption - generates a queueHooks object, or returns the current one
+	_queueHooks: function( elem, type ) {
+		var key = type + "queueHooks";
+		return data_priv.get( elem, key ) || data_priv.access( elem, key, {
+			empty: jQuery.Callbacks("once memory").add(function() {
+				data_priv.remove( elem, [ type + "queue", key ] );
+			})
+		});
+	}
+});
+
+jQuery.fn.extend({
+	queue: function( type, data ) {
+		var setter = 2;
+
+		if ( typeof type !== "string" ) {
+			data = type;
+			type = "fx";
+			setter--;
+		}
+
+		if ( arguments.length < setter ) {
+			return jQuery.queue( this[0], type );
+		}
+
+		return data === undefined ?
+			this :
+			this.each(function() {
+				var queue = jQuery.queue( this, type, data );
+
+				// ensure a hooks for this queue
+				jQuery._queueHooks( this, type );
+
+				if ( type === "fx" && queue[0] !== "inprogress" ) {
+					jQuery.dequeue( this, type );
+				}
+			});
+	},
+	dequeue: function( type ) {
+		return this.each(function() {
+			jQuery.dequeue( this, type );
+		});
+	},
+	clearQueue: function( type ) {
+		return this.queue( type || "fx", [] );
+	},
+	// Get a promise resolved when queues of a certain type
+	// are emptied (fx is the type by default)
+	promise: function( type, obj ) {
+		var tmp,
+			count = 1,
+			defer = jQuery.Deferred(),
+			elements = this,
+			i = this.length,
+			resolve = function() {
+				if ( !( --count ) ) {
+					defer.resolveWith( elements, [ elements ] );
+				}
+			};
+
+		if ( typeof type !== "string" ) {
+			obj = type;
+			type = undefined;
+		}
+		type = type || "fx";
+
+		while ( i-- ) {
+			tmp = data_priv.get( elements[ i ], type + "queueHooks" );
+			if ( tmp && tmp.empty ) {
+				count++;
+				tmp.empty.add( resolve );
+			}
+		}
+		resolve();
+		return defer.promise( obj );
+	}
+});
+var pnum = (/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/).source;
+
+var cssExpand = [ "Top", "Right", "Bottom", "Left" ];
+
+var isHidden = function( elem, el ) {
+		// isHidden might be called from jQuery#filter function;
+		// in that case, element will be second argument
+		elem = el || elem;
+		return jQuery.css( elem, "display" ) === "none" || !jQuery.contains( elem.ownerDocument, elem );
+	};
+
+var rcheckableType = (/^(?:checkbox|radio)$/i);
+
+
+
+(function() {
+	var fragment = document.createDocumentFragment(),
+		div = fragment.appendChild( document.createElement( "div" ) ),
+		input = document.createElement( "input" );
+
+	// #11217 - WebKit loses check when the name is after the checked attribute
+	// Support: Windows Web Apps (WWA)
+	// `name` and `type` need .setAttribute for WWA
+	input.setAttribute( "type", "radio" );
+	input.setAttribute( "checked", "checked" );
+	input.setAttribute( "name", "t" );
+
+	div.appendChild( input );
+
+	// Support: Safari 5.1, iOS 5.1, Android 4.x, Android 2.3
+	// old WebKit doesn't clone checked state correctly in fragments
+	support.checkClone = div.cloneNode( true ).cloneNode( true ).lastChild.checked;
+
+	// Make sure textarea (and checkbox) defaultValue is properly cloned
+	// Support: IE9-IE11+
+	div.innerHTML = "<textarea>x</textarea>";
+	support.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue;
+})();
+var strundefined = typeof undefined;
+
+
+
+support.focusinBubbles = "onfocusin" in window;
+
+
+var
+	rkeyEvent = /^key/,
+	rmouseEvent = /^(?:mouse|pointer|contextmenu)|click/,
+	rfocusMorph = /^(?:focusinfocus|focusoutblur)$/,
+	rtypenamespace = /^([^.]*)(?:\.(.+)|)$/;
+
+function returnTrue() {
+	return true;
+}
+
+function returnFalse() {
+	return false;
+}
+
+function safeActiveElement() {
+	try {
+		return document.activeElement;
+	} catch ( err ) { }
+}
+
+/*
+ * Helper functions for managing events -- not part of the public interface.
+ * Props to Dean Edwards' addEvent library for many of the ideas.
+ */
+jQuery.event = {
+
+	global: {},
+
+	add: function( elem, types, handler, data, selector ) {
+
+		var handleObjIn, eventHandle, tmp,
+			events, t, handleObj,
+			special, handlers, type, namespaces, origType,
+			elemData = data_priv.get( elem );
+
+		// Don't attach events to noData or text/comment nodes (but allow plain objects)
+		if ( !elemData ) {
+			return;
+		}
+
+		// Caller can pass in an object of custom data in lieu of the handler
+		if ( handler.handler ) {
+			handleObjIn = handler;
+			handler = handleObjIn.handler;
+			selector = handleObjIn.selector;
+		}
+
+		// Make sure that the handler has a unique ID, used to find/remove it later
+		if ( !handler.guid ) {
+			handler.guid = jQuery.guid++;
+		}
+
+		// Init the element's event structure and main handler, if this is the first
+		if ( !(events = elemData.events) ) {
+			events = elemData.events = {};
+		}
+		if ( !(eventHandle = elemData.handle) ) {
+			eventHandle = elemData.handle = function( e ) {
+				// Discard the second event of a jQuery.event.trigger() and
+				// when an event is called after a page has unloaded
+				return typeof jQuery !== strundefined && jQuery.event.triggered !== e.type ?
+					jQuery.event.dispatch.apply( elem, arguments ) : undefined;
+			};
+		}
+
+		// Handle multiple events separated by a space
+		types = ( types || "" ).match( rnotwhite ) || [ "" ];
+		t = types.length;
+		while ( t-- ) {
+			tmp = rtypenamespace.exec( types[t] ) || [];
+			type = origType = tmp[1];
+			namespaces = ( tmp[2] || "" ).split( "." ).sort();
+
+			// There *must* be a type, no attaching namespace-only handlers
+			if ( !type ) {
+				continue;
+			}
+
+			// If event changes its type, use the special event handlers for the changed type
+			special = jQuery.event.special[ type ] || {};
+
+			// If selector defined, determine special event api type, otherwise given type
+			type = ( selector ? special.delegateType : special.bindType ) || type;
+
+			// Update special based on newly reset type
+			special = jQuery.event.special[ type ] || {};
+
+			// handleObj is passed to all event handlers
+			handleObj = jQuery.extend({
+				type: type,
+				origType: origType,
+				data: data,
+				handler: handler,
+				guid: handler.guid,
+				selector: selector,
+				needsContext: selector && jQuery.expr.match.needsContext.test( selector ),
+				namespace: namespaces.join(".")
+			}, handleObjIn );
+
+			// Init the event handler queue if we're the first
+			if ( !(handlers = events[ type ]) ) {
+				handlers = events[ type ] = [];
+				handlers.delegateCount = 0;
+
+				// Only use addEventListener if the special events handler returns false
+				if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) {
+					if ( elem.addEventListener ) {
+						elem.addEventListener( type, eventHandle, false );
+					}
+				}
+			}
+
+			if ( special.add ) {
+				special.add.call( elem, handleObj );
+
+				if ( !handleObj.handler.guid ) {
+					handleObj.handler.guid = handler.guid;
+				}
+			}
+
+			// Add to the element's handler list, delegates in front
+			if ( selector ) {
+				handlers.splice( handlers.delegateCount++, 0, handleObj );
+			} else {
+				handlers.push( handleObj );
+			}
+
+			// Keep track of which events have ever been used, for event optimization
+			jQuery.event.global[ type ] = true;
+		}
+
+	},
+
+	// Detach an event or set of events from an element
+	remove: function( elem, types, handler, selector, mappedTypes ) {
+
+		var j, origCount, tmp,
+			events, t, handleObj,
+			special, handlers, type, namespaces, origType,
+			elemData = data_priv.hasData( elem ) && data_priv.get( elem );
+
+		if ( !elemData || !(events = elemData.events) ) {
+			return;
+		}
+
+		// Once for each type.namespace in types; type may be omitted
+		types = ( types || "" ).match( rnotwhite ) || [ "" ];
+		t = types.length;
+		while ( t-- ) {
+			tmp = rtypenamespace.exec( types[t] ) || [];
+			type = origType = tmp[1];
+			namespaces = ( tmp[2] || "" ).split( "." ).sort();
+
+			// Unbind all events (on this namespace, if provided) for the element
+			if ( !type ) {
+				for ( type in events ) {
+					jQuery.event.remove( elem, type + types[ t ], handler, selector, true );
+				}
+				continue;
+			}
+
+			special = jQuery.event.special[ type ] || {};
+			type = ( selector ? special.delegateType : special.bindType ) || type;
+			handlers = events[ type ] || [];
+			tmp = tmp[2] && new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" );
+
+			// Remove matching events
+			origCount = j = handlers.length;
+			while ( j-- ) {
+				handleObj = handlers[ j ];
+
+				if ( ( mappedTypes || origType === handleObj.origType ) &&
+					( !handler || handler.guid === handleObj.guid ) &&
+					( !tmp || tmp.test( handleObj.namespace ) ) &&
+					( !selector || selector === handleObj.selector || selector === "**" && handleObj.selector ) ) {
+					handlers.splice( j, 1 );
+
+					if ( handleObj.selector ) {
+						handlers.delegateCount--;
+					}
+					if ( special.remove ) {
+						special.remove.call( elem, handleObj );
+					}
+				}
+			}
+
+			// Remove generic event handler if we removed something and no more handlers exist
+			// (avoids potential for endless recursion during removal of special event handlers)
+			if ( origCount && !handlers.length ) {
+				if ( !special.teardown || special.teardown.call( elem, namespaces, elemData.handle ) === false ) {
+					jQuery.removeEvent( elem, type, elemData.handle );
+				}
+
+				delete events[ type ];
+			}
+		}
+
+		// Remove the expando if it's no longer used
+		if ( jQuery.isEmptyObject( events ) ) {
+			delete elemData.handle;
+			data_priv.remove( elem, "events" );
+		}
+	},
+
+	trigger: function( event, data, elem, onlyHandlers ) {
+
+		var i, cur, tmp, bubbleType, ontype, handle, special,
+			eventPath = [ elem || document ],
+			type = hasOwn.call( event, "type" ) ? event.type : event,
+			namespaces = hasOwn.call( event, "namespace" ) ? event.namespace.split(".") : [];
+
+		cur = tmp = elem = elem || document;
+
+		// Don't do events on text and comment nodes
+		if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
+			return;
+		}
+
+		// focus/blur morphs to focusin/out; ensure we're not firing them right now
+		if ( rfocusMorph.test( type + jQuery.event.triggered ) ) {
+			return;
+		}
+
+		if ( type.indexOf(".") >= 0 ) {
+			// Namespaced trigger; create a regexp to match event type in handle()
+			namespaces = type.split(".");
+			type = namespaces.shift();
+			namespaces.sort();
+		}
+		ontype = type.indexOf(":") < 0 && "on" + type;
+
+		// Caller can pass in a jQuery.Event object, Object, or just an event type string
+		event = event[ jQuery.expando ] ?
+			event :
+			new jQuery.Event( type, typeof event === "object" && event );
+
+		// Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true)
+		event.isTrigger = onlyHandlers ? 2 : 3;
+		event.namespace = namespaces.join(".");
+		event.namespace_re = event.namespace ?
+			new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" ) :
+			null;
+
+		// Clean up the event in case it is being reused
+		event.result = undefined;
+		if ( !event.target ) {
+			event.target = elem;
+		}
+
+		// Clone any incoming data and prepend the event, creating the handler arg list
+		data = data == null ?
+			[ event ] :
+			jQuery.makeArray( data, [ event ] );
+
+		// Allow special events to draw outside the lines
+		special = jQuery.event.special[ type ] || {};
+		if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) {
+			return;
+		}
+
+		// Determine event propagation path in advance, per W3C events spec (#9951)
+		// Bubble up to document, then to window; watch for a global ownerDocument var (#9724)
+		if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) {
+
+			bubbleType = special.delegateType || type;
+			if ( !rfocusMorph.test( bubbleType + type ) ) {
+				cur = cur.parentNode;
+			}
+			for ( ; cur; cur = cur.parentNode ) {
+				eventPath.push( cur );
+				tmp = cur;
+			}
+
+			// Only add window if we got to document (e.g., not plain obj or detached DOM)
+			if ( tmp === (elem.ownerDocument || document) ) {
+				eventPath.push( tmp.defaultView || tmp.parentWindow || window );
+			}
+		}
+
+		// Fire handlers on the event path
+		i = 0;
+		while ( (cur = eventPath[i++]) && !event.isPropagationStopped() ) {
+
+			event.type = i > 1 ?
+				bubbleType :
+				special.bindType || type;
+
+			// jQuery handler
+			handle = ( data_priv.get( cur, "events" ) || {} )[ event.type ] && data_priv.get( cur, "handle" );
+			if ( handle ) {
+				handle.apply( cur, data );
+			}
+
+			// Native handler
+			handle = ontype && cur[ ontype ];
+			if ( handle && handle.apply && jQuery.acceptData( cur ) ) {
+				event.result = handle.apply( cur, data );
+				if ( event.result === false ) {
+					event.preventDefault();
+				}
+			}
+		}
+		event.type = type;
+
+		// If nobody prevented the default action, do it now
+		if ( !onlyHandlers && !event.isDefaultPrevented() ) {
+
+			if ( (!special._default || special._default.apply( eventPath.pop(), data ) === false) &&
+				jQuery.acceptData( elem ) ) {
+
+				// Call a native DOM method on the target with the same name name as the event.
+				// Don't do default actions on window, that's where global variables be (#6170)
+				if ( ontype && jQuery.isFunction( elem[ type ] ) && !jQuery.isWindow( elem ) ) {
+
+					// Don't re-trigger an onFOO event when we call its FOO() method
+					tmp = elem[ ontype ];
+
+					if ( tmp ) {
+						elem[ ontype ] = null;
+					}
+
+					// Prevent re-triggering of the same event, since we already bubbled it above
+					jQuery.event.triggered = type;
+					elem[ type ]();
+					jQuery.event.triggered = undefined;
+
+					if ( tmp ) {
+						elem[ ontype ] = tmp;
+					}
+				}
+			}
+		}
+
+		return event.result;
+	},
+
+	dispatch: function( event ) {
+
+		// Make a writable jQuery.Event from the native event object
+		event = jQuery.event.fix( event );
+
+		var i, j, ret, matched, handleObj,
+			handlerQueue = [],
+			args = slice.call( arguments ),
+			handlers = ( data_priv.get( this, "events" ) || {} )[ event.type ] || [],
+			special = jQuery.event.special[ event.type ] || {};
+
+		// Use the fix-ed jQuery.Event rather than the (read-only) native event
+		args[0] = event;
+		event.delegateTarget = this;
+
+		// Call the preDispatch hook for the mapped type, and let it bail if desired
+		if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) {
+			return;
+		}
+
+		// Determine handlers
+		handlerQueue = jQuery.event.handlers.call( this, event, handlers );
+
+		// Run delegates first; they may want to stop propagation beneath us
+		i = 0;
+		while ( (matched = handlerQueue[ i++ ]) && !event.isPropagationStopped() ) {
+			event.currentTarget = matched.elem;
+
+			j = 0;
+			while ( (handleObj = matched.handlers[ j++ ]) && !event.isImmediatePropagationStopped() ) {
+
+				// Triggered event must either 1) have no namespace, or
+				// 2) have namespace(s) a subset or equal to those in the bound event (both can have no namespace).
+				if ( !event.namespace_re || event.namespace_re.test( handleObj.namespace ) ) {
+
+					event.handleObj = handleObj;
+					event.data = handleObj.data;
+
+					ret = ( (jQuery.event.special[ handleObj.origType ] || {}).handle || handleObj.handler )
+							.apply( matched.elem, args );
+
+					if ( ret !== undefined ) {
+						if ( (event.result = ret) === false ) {
+							event.preventDefault();
+							event.stopPropagation();
+						}
+					}
+				}
+			}
+		}
+
+		// Call the postDispatch hook for the mapped type
+		if ( special.postDispatch ) {
+			special.postDispatch.call( this, event );
+		}
+
+		return event.result;
+	},
+
+	handlers: function( event, handlers ) {
+		var i, matches, sel, handleObj,
+			handlerQueue = [],
+			delegateCount = handlers.delegateCount,
+			cur = event.target;
+
+		// Find delegate handlers
+		// Black-hole SVG <use> instance trees (#13180)
+		// Avoid non-left-click bubbling in Firefox (#3861)
+		if ( delegateCount && cur.nodeType && (!event.button || event.type !== "click") ) {
+
+			for ( ; cur !== this; cur = cur.parentNode || this ) {
+
+				// Don't process clicks on disabled elements (#6911, #8165, #11382, #11764)
+				if ( cur.disabled !== true || event.type !== "click" ) {
+					matches = [];
+					for ( i = 0; i < delegateCount; i++ ) {
+						handleObj = handlers[ i ];
+
+						// Don't conflict with Object.prototype properties (#13203)
+						sel = handleObj.selector + " ";
+
+						if ( matches[ sel ] === undefined ) {
+							matches[ sel ] = handleObj.needsContext ?
+								jQuery( sel, this ).index( cur ) >= 0 :
+								jQuery.find( sel, this, null, [ cur ] ).length;
+						}
+						if ( matches[ sel ] ) {
+							matches.push( handleObj );
+						}
+					}
+					if ( matches.length ) {
+						handlerQueue.push({ elem: cur, handlers: matches });
+					}
+				}
+			}
+		}
+
+		// Add the remaining (directly-bound) handlers
+		if ( delegateCount < handlers.length ) {
+			handlerQueue.push({ elem: this, handlers: handlers.slice( delegateCount ) });
+		}
+
+		return handlerQueue;
+	},
+
+	// Includes some event props shared by KeyEvent and MouseEvent
+	props: "altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),
+
+	fixHooks: {},
+
+	keyHooks: {
+		props: "char charCode key keyCode".split(" "),
+		filter: function( event, original ) {
+
+			// Add which for key events
+			if ( event.which == null ) {
+				event.which = original.charCode != null ? original.charCode : original.keyCode;
+			}
+
+			return event;
+		}
+	},
+
+	mouseHooks: {
+		props: "button buttons clientX clientY offsetX offsetY pageX pageY screenX screenY toElement".split(" "),
+		filter: function( event, original ) {
+			var eventDoc, doc, body,
+				button = original.button;
+
+			// Calculate pageX/Y if missing and clientX/Y available
+			if ( event.pageX == null && original.clientX != null ) {
+				eventDoc = event.target.ownerDocument || document;
+				doc = eventDoc.documentElement;
+				body = eventDoc.body;
+
+				event.pageX = original.clientX + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - ( doc && doc.clientLeft || body && body.clientLeft || 0 );
+				event.pageY = original.clientY + ( doc && doc.scrollTop  || body && body.scrollTop  || 0 ) - ( doc && doc.clientTop  || body && body.clientTop  || 0 );
+			}
+
+			// Add which for click: 1 === left; 2 === middle; 3 === right
+			// Note: button is not normalized, so don't use it
+			if ( !event.which && button !== undefined ) {
+				event.which = ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) );
+			}
+
+			return event;
+		}
+	},
+
+	fix: function( event ) {
+		if ( event[ jQuery.expando ] ) {
+			return event;
+		}
+
+		// Create a writable copy of the event object and normalize some properties
+		var i, prop, copy,
+			type = event.type,
+			originalEvent = event,
+			fixHook = this.fixHooks[ type ];
+
+		if ( !fixHook ) {
+			this.fixHooks[ type ] = fixHook =
+				rmouseEvent.test( type ) ? this.mouseHooks :
+				rkeyEvent.test( type ) ? this.keyHooks :
+				{};
+		}
+		copy = fixHook.props ? this.props.concat( fixHook.props ) : this.props;
+
+		event = new jQuery.Event( originalEvent );
+
+		i = copy.length;
+		while ( i-- ) {
+			prop = copy[ i ];
+			event[ prop ] = originalEvent[ prop ];
+		}
+
+		// Support: Cordova 2.5 (WebKit) (#13255)
+		// All events should have a target; Cordova deviceready doesn't
+		if ( !event.target ) {
+			event.target = document;
+		}
+
+		// Support: Safari 6.0+, Chrome < 28
+		// Target should not be a text node (#504, #13143)
+		if ( event.target.nodeType === 3 ) {
+			event.target = event.target.parentNode;
+		}
+
+		return fixHook.filter ? fixHook.filter( event, originalEvent ) : event;
+	},
+
+	special: {
+		load: {
+			// Prevent triggered image.load events from bubbling to window.load
+			noBubble: true
+		},
+		focus: {
+			// Fire native event if possible so blur/focus sequence is correct
+			trigger: function() {
+				if ( this !== safeActiveElement() && this.focus ) {
+					this.focus();
+					return false;
+				}
+			},
+			delegateType: "focusin"
+		},
+		blur: {
+			trigger: function() {
+				if ( this === safeActiveElement() && this.blur ) {
+					this.blur();
+					return false;
+				}
+			},
+			delegateType: "focusout"
+		},
+		click: {
+			// For checkbox, fire native event so checked state will be right
+			trigger: function() {
+				if ( this.type === "checkbox" && this.click && jQuery.nodeName( this, "input" ) ) {
+					this.click();
+					return false;
+				}
+			},
+
+			// For cross-browser consistency, don't fire native .click() on links
+			_default: function( event ) {
+				return jQuery.nodeName( event.target, "a" );
+			}
+		},
+
+		beforeunload: {
+			postDispatch: function( event ) {
+
+				// Support: Firefox 20+
+				// Firefox doesn't alert if the returnValue field is not set.
+				if ( event.result !== undefined && event.originalEvent ) {
+					event.originalEvent.returnValue = event.result;
+				}
+			}
+		}
+	},
+
+	simulate: function( type, elem, event, bubble ) {
+		// Piggyback on a donor event to simulate a different one.
+		// Fake originalEvent to avoid donor's stopPropagation, but if the
+		// simulated event prevents default then we do the same on the donor.
+		var e = jQuery.extend(
+			new jQuery.Event(),
+			event,
+			{
+				type: type,
+				isSimulated: true,
+				originalEvent: {}
+			}
+		);
+		if ( bubble ) {
+			jQuery.event.trigger( e, null, elem );
+		} else {
+			jQuery.event.dispatch.call( elem, e );
+		}
+		if ( e.isDefaultPrevented() ) {
+			event.preventDefault();
+		}
+	}
+};
+
+jQuery.removeEvent = function( elem, type, handle ) {
+	if ( elem.removeEventListener ) {
+		elem.removeEventListener( type, handle, false );
+	}
+};
+
+jQuery.Event = function( src, props ) {
+	// Allow instantiation without the 'new' keyword
+	if ( !(this instanceof jQuery.Event) ) {
+		return new jQuery.Event( src, props );
+	}
+
+	// Event object
+	if ( src && src.type ) {
+		this.originalEvent = src;
+		this.type = src.type;
+
+		// Events bubbling up the document may have been marked as prevented
+		// by a handler lower down the tree; reflect the correct value.
+		this.isDefaultPrevented = src.defaultPrevented ||
+				src.defaultPrevented === undefined &&
+				// Support: Android < 4.0
+				src.returnValue === false ?
+			returnTrue :
+			returnFalse;
+
+	// Event type
+	} else {
+		this.type = src;
+	}
+
+	// Put explicitly provided properties onto the event object
+	if ( props ) {
+		jQuery.extend( this, props );
+	}
+
+	// Create a timestamp if incoming event doesn't have one
+	this.timeStamp = src && src.timeStamp || jQuery.now();
+
+	// Mark it as fixed
+	this[ jQuery.expando ] = true;
+};
+
+// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding
+// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
+jQuery.Event.prototype = {
+	isDefaultPrevented: returnFalse,
+	isPropagationStopped: returnFalse,
+	isImmediatePropagationStopped: returnFalse,
+
+	preventDefault: function() {
+		var e = this.originalEvent;
+
+		this.isDefaultPrevented = returnTrue;
+
+		if ( e && e.preventDefault ) {
+			e.preventDefault();
+		}
+	},
+	stopPropagation: function() {
+		var e = this.originalEvent;
+
+		this.isPropagationStopped = returnTrue;
+
+		if ( e && e.stopPropagation ) {
+			e.stopPropagation();
+		}
+	},
+	stopImmediatePropagation: function() {
+		var e = this.originalEvent;
+
+		this.isImmediatePropagationStopped = returnTrue;
+
+		if ( e && e.stopImmediatePropagation ) {
+			e.stopImmediatePropagation();
+		}
+
+		this.stopPropagation();
+	}
+};
+
+// Create mouseenter/leave events using mouseover/out and event-time checks
+// Support: Chrome 15+
+jQuery.each({
+	mouseenter: "mouseover",
+	mouseleave: "mouseout",
+	pointerenter: "pointerover",
+	pointerleave: "pointerout"
+}, function( orig, fix ) {
+	jQuery.event.special[ orig ] = {
+		delegateType: fix,
+		bindType: fix,
+
+		handle: function( event ) {
+			var ret,
+				target = this,
+				related = event.relatedTarget,
+				handleObj = event.handleObj;
+
+			// For mousenter/leave call the handler if related is outside the target.
+			// NB: No relatedTarget if the mouse left/entered the browser window
+			if ( !related || (related !== target && !jQuery.contains( target, related )) ) {
+				event.type = handleObj.origType;
+				ret = handleObj.handler.apply( this, arguments );
+				event.type = fix;
+			}
+			return ret;
+		}
+	};
+});
+
+// Create "bubbling" focus and blur events
+// Support: Firefox, Chrome, Safari
+if ( !support.focusinBubbles ) {
+	jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) {
+
+		// Attach a single capturing handler on the document while someone wants focusin/focusout
+		var handler = function( event ) {
+				jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ), true );
+			};
+
+		jQuery.event.special[ fix ] = {
+			setup: function() {
+				var doc = this.ownerDocument || this,
+					attaches = data_priv.access( doc, fix );
+
+				if ( !attaches ) {
+					doc.addEventListener( orig, handler, true );
+				}
+				data_priv.access( doc, fix, ( attaches || 0 ) + 1 );
+			},
+			teardown: function() {
+				var doc = this.ownerDocument || this,
+					attaches = data_priv.access( doc, fix ) - 1;
+
+				if ( !attaches ) {
+					doc.removeEventListener( orig, handler, true );
+					data_priv.remove( doc, fix );
+
+				} else {
+					data_priv.access( doc, fix, attaches );
+				}
+			}
+		};
+	});
+}
+
+jQuery.fn.extend({
+
+	on: function( types, selector, data, fn, /*INTERNAL*/ one ) {
+		var origFn, type;
+
+		// Types can be a map of types/handlers
+		if ( typeof types === "object" ) {
+			// ( types-Object, selector, data )
+			if ( typeof selector !== "string" ) {
+				// ( types-Object, data )
+				data = data || selector;
+				selector = undefined;
+			}
+			for ( type in types ) {
+				this.on( type, selector, data, types[ type ], one );
+			}
+			return this;
+		}
+
+		if ( data == null && fn == null ) {
+			// ( types, fn )
+			fn = selector;
+			data = selector = undefined;
+		} else if ( fn == null ) {
+			if ( typeof selector === "string" ) {
+				// ( types, selector, fn )
+				fn = data;
+				data = undefined;
+			} else {
+				// ( types, data, fn )
+				fn = data;
+				data = selector;
+				selector = undefined;
+			}
+		}
+		if ( fn === false ) {
+			fn = returnFalse;
+		} else if ( !fn ) {
+			return this;
+		}
+
+		if ( one === 1 ) {
+			origFn = fn;
+			fn = function( event ) {
+				// Can use an empty set, since event contains the info
+				jQuery().off( event );
+				return origFn.apply( this, arguments );
+			};
+			// Use same guid so caller can remove using origFn
+			fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ );
+		}
+		return this.each( function() {
+			jQuery.event.add( this, types, fn, data, selector );
+		});
+	},
+	one: function( types, selector, data, fn ) {
+		return this.on( types, selector, data, fn, 1 );
+	},
+	off: function( types, selector, fn ) {
+		var handleObj, type;
+		if ( types && types.preventDefault && types.handleObj ) {
+			// ( event )  dispatched jQuery.Event
+			handleObj = types.handleObj;
+			jQuery( types.delegateTarget ).off(
+				handleObj.namespace ? handleObj.origType + "." + handleObj.namespace : handleObj.origType,
+				handleObj.selector,
+				handleObj.handler
+			);
+			return this;
+		}
+		if ( typeof types === "object" ) {
+			// ( types-object [, selector] )
+			for ( type in types ) {
+				this.off( type, selector, types[ type ] );
+			}
+			return this;
+		}
+		if ( selector === false || typeof selector === "function" ) {
+			// ( types [, fn] )
+			fn = selector;
+			selector = undefined;
+		}
+		if ( fn === false ) {
+			fn = returnFalse;
+		}
+		return this.each(function() {
+			jQuery.event.remove( this, types, fn, selector );
+		});
+	},
+
+	trigger: function( type, data ) {
+		return this.each(function() {
+			jQuery.event.trigger( type, data, this );
+		});
+	},
+	triggerHandler: function( type, data ) {
+		var elem = this[0];
+		if ( elem ) {
+			return jQuery.event.trigger( type, data, elem, true );
+		}
+	}
+});
+
+
+var
+	rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,
+	rtagName = /<([\w:]+)/,
+	rhtml = /<|&#?\w+;/,
+	rnoInnerhtml = /<(?:script|style|link)/i,
+	// checked="checked" or checked
+	rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i,
+	rscriptType = /^$|\/(?:java|ecma)script/i,
+	rscriptTypeMasked = /^true\/(.*)/,
+	rcleanScript = /^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g,
+
+	// We have to close these tags to support XHTML (#13200)
+	wrapMap = {
+
+		// Support: IE 9
+		option: [ 1, "<select multiple='multiple'>", "</select>" ],
+
+		thead: [ 1, "<table>", "</table>" ],
+		col: [ 2, "<table><colgroup>", "</colgroup></table>" ],
+		tr: [ 2, "<table><tbody>", "</tbody></table>" ],
+		td: [ 3, "<table><tbody><tr>", "</tr></tbody></table>" ],
+
+		_default: [ 0, "", "" ]
+	};
+
+// Support: IE 9
+wrapMap.optgroup = wrapMap.option;
+
+wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
+wrapMap.th = wrapMap.td;
+
+// Support: 1.x compatibility
+// Manipulating tables requires a tbody
+function manipulationTarget( elem, content ) {
+	return jQuery.nodeName( elem, "table" ) &&
+		jQuery.nodeName( content.nodeType !== 11 ? content : content.firstChild, "tr" ) ?
+
+		elem.getElementsByTagName("tbody")[0] ||
+			elem.appendChild( elem.ownerDocument.createElement("tbody") ) :
+		elem;
+}
+
+// Replace/restore the type attribute of script elements for safe DOM manipulation
+function disableScript( elem ) {
+	elem.type = (elem.getAttribute("type") !== null) + "/" + elem.type;
+	return elem;
+}
+function restoreScript( elem ) {
+	var match = rscriptTypeMasked.exec( elem.type );
+
+	if ( match ) {
+		elem.type = match[ 1 ];
+	} else {
+		elem.removeAttribute("type");
+	}
+
+	return elem;
+}
+
+// Mark scripts as having already been evaluated
+function setGlobalEval( elems, refElements ) {
+	var i = 0,
+		l = elems.length;
+
+	for ( ; i < l; i++ ) {
+		data_priv.set(
+			elems[ i ], "globalEval", !refElements || data_priv.get( refElements[ i ], "globalEval" )
+		);
+	}
+}
+
+function cloneCopyEvent( src, dest ) {
+	var i, l, type, pdataOld, pdataCur, udataOld, udataCur, events;
+
+	if ( dest.nodeType !== 1 ) {
+		return;
+	}
+
+	// 1. Copy private data: events, handlers, etc.
+	if ( data_priv.hasData( src ) ) {
+		pdataOld = data_priv.access( src );
+		pdataCur = data_priv.set( dest, pdataOld );
+		events = pdataOld.events;
+
+		if ( events ) {
+			delete pdataCur.handle;
+			pdataCur.events = {};
+
+			for ( type in events ) {
+				for ( i = 0, l = events[ type ].length; i < l; i++ ) {
+					jQuery.event.add( dest, type, events[ type ][ i ] );
+				}
+			}
+		}
+	}
+
+	// 2. Copy user data
+	if ( data_user.hasData( src ) ) {
+		udataOld = data_user.access( src );
+		udataCur = jQuery.extend( {}, udataOld );
+
+		data_user.set( dest, udataCur );
+	}
+}
+
+function getAll( context, tag ) {
+	var ret = context.getElementsByTagName ? context.getElementsByTagName( tag || "*" ) :
+			context.querySelectorAll ? context.querySelectorAll( tag || "*" ) :
+			[];
+
+	return tag === undefined || tag && jQuery.nodeName( context, tag ) ?
+		jQuery.merge( [ context ], ret ) :
+		ret;
+}
+
+// Support: IE >= 9
+function fixInput( src, dest ) {
+	var nodeName = dest.nodeName.toLowerCase();
+
+	// Fails to persist the checked state of a cloned checkbox or radio button.
+	if ( nodeName === "input" && rcheckableType.test( src.type ) ) {
+		dest.checked = src.checked;
+
+	// Fails to return the selected option to the default selected state when cloning options
+	} else if ( nodeName === "input" || nodeName === "textarea" ) {
+		dest.defaultValue = src.defaultValue;
+	}
+}
+
+jQuery.extend({
+	clone: function( elem, dataAndEvents, deepDataAndEvents ) {
+		var i, l, srcElements, destElements,
+			clone = elem.cloneNode( true ),
+			inPage = jQuery.contains( elem.ownerDocument, elem );
+
+		// Support: IE >= 9
+		// Fix Cloning issues
+		if ( !support.noCloneChecked && ( elem.nodeType === 1 || elem.nodeType === 11 ) &&
+				!jQuery.isXMLDoc( elem ) ) {
+
+			// We eschew Sizzle here for performance reasons: http://jsperf.com/getall-vs-sizzle/2
+			destElements = getAll( clone );
+			srcElements = getAll( elem );
+
+			for ( i = 0, l = srcElements.length; i < l; i++ ) {
+				fixInput( srcElements[ i ], destElements[ i ] );
+			}
+		}
+
+		// Copy the events from the original to the clone
+		if ( dataAndEvents ) {
+			if ( deepDataAndEvents ) {
+				srcElements = srcElements || getAll( elem );
+				destElements = destElements || getAll( clone );
+
+				for ( i = 0, l = srcElements.length; i < l; i++ ) {
+					cloneCopyEvent( srcElements[ i ], destElements[ i ] );
+				}
+			} else {
+				cloneCopyEvent( elem, clone );
+			}
+		}
+
+		// Preserve script evaluation history
+		destElements = getAll( clone, "script" );
+		if ( destElements.length > 0 ) {
+			setGlobalEval( destElements, !inPage && getAll( elem, "script" ) );
+		}
+
+		// Return the cloned set
+		return clone;
+	},
+
+	buildFragment: function( elems, context, scripts, selection ) {
+		var elem, tmp, tag, wrap, contains, j,
+			fragment = context.createDocumentFragment(),
+			nodes = [],
+			i = 0,
+			l = elems.length;
+
+		for ( ; i < l; i++ ) {
+			elem = elems[ i ];
+
+			if ( elem || elem === 0 ) {
+
+				// Add nodes directly
+				if ( jQuery.type( elem ) === "object" ) {
+					// Support: QtWebKit
+					// jQuery.merge because push.apply(_, arraylike) throws
+					jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem );
+
+				// Convert non-html into a text node
+				} else if ( !rhtml.test( elem ) ) {
+					nodes.push( context.createTextNode( elem ) );
+
+				// Convert html into DOM nodes
+				} else {
+					tmp = tmp || fragment.appendChild( context.createElement("div") );
+
+					// Deserialize a standard representation
+					tag = ( rtagName.exec( elem ) || [ "", "" ] )[ 1 ].toLowerCase();
+					wrap = wrapMap[ tag ] || wrapMap._default;
+					tmp.innerHTML = wrap[ 1 ] + elem.replace( rxhtmlTag, "<$1></$2>" ) + wrap[ 2 ];
+
+					// Descend through wrappers to the right content
+					j = wrap[ 0 ];
+					while ( j-- ) {
+						tmp = tmp.lastChild;
+					}
+
+					// Support: QtWebKit
+					// jQuery.merge because push.apply(_, arraylike) throws
+					jQuery.merge( nodes, tmp.childNodes );
+
+					// Remember the top-level container
+					tmp = fragment.firstChild;
+
+					// Fixes #12346
+					// Support: Webkit, IE
+					tmp.textContent = "";
+				}
+			}
+		}
+
+		// Remove wrapper from fragment
+		fragment.textContent = "";
+
+		i = 0;
+		while ( (elem = nodes[ i++ ]) ) {
+
+			// #4087 - If origin and destination elements are the same, and this is
+			// that element, do not do anything
+			if ( selection && jQuery.inArray( elem, selection ) !== -1 ) {
+				continue;
+			}
+
+			contains = jQuery.contains( elem.ownerDocument, elem );
+
+			// Append to fragment
+			tmp = getAll( fragment.appendChild( elem ), "script" );
+
+			// Preserve script evaluation history
+			if ( contains ) {
+				setGlobalEval( tmp );
+			}
+
+			// Capture executables
+			if ( scripts ) {
+				j = 0;
+				while ( (elem = tmp[ j++ ]) ) {
+					if ( rscriptType.test( elem.type || "" ) ) {
+						scripts.push( elem );
+					}
+				}
+			}
+		}
+
+		return fragment;
+	},
+
+	cleanData: function( elems ) {
+		var data, elem, type, key,
+			special = jQuery.event.special,
+			i = 0;
+
+		for ( ; (elem = elems[ i ]) !== undefined; i++ ) {
+			if ( jQuery.acceptData( elem ) ) {
+				key = elem[ data_priv.expando ];
+
+				if ( key && (data = data_priv.cache[ key ]) ) {
+					if ( data.events ) {
+						for ( type in data.events ) {
+							if ( special[ type ] ) {
+								jQuery.event.remove( elem, type );
+
+							// This is a shortcut to avoid jQuery.event.remove's overhead
+							} else {
+								jQuery.removeEvent( elem, type, data.handle );
+							}
+						}
+					}
+					if ( data_priv.cache[ key ] ) {
+						// Discard any remaining `private` data
+						delete data_priv.cache[ key ];
+					}
+				}
+			}
+			// Discard any remaining `user` data
+			delete data_user.cache[ elem[ data_user.expando ] ];
+		}
+	}
+});
+
+jQuery.fn.extend({
+	text: function( value ) {
+		return access( this, function( value ) {
+			return value === undefined ?
+				jQuery.text( this ) :
+				this.empty().each(function() {
+					if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {
+						this.textContent = value;
+					}
+				});
+		}, null, value, arguments.length );
+	},
+
+	append: function() {
+		return this.domManip( arguments, function( elem ) {
+			if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {
+				var target = manipulationTarget( this, elem );
+				target.appendChild( elem );
+			}
+		});
+	},
+
+	prepend: function() {
+		return this.domManip( arguments, function( elem ) {
+			if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {
+				var target = manipulationTarget( this, elem );
+				target.insertBefore( elem, target.firstChild );
+			}
+		});
+	},
+
+	before: function() {
+		return this.domManip( arguments, function( elem ) {
+			if ( this.parentNode ) {
+				this.parentNode.insertBefore( elem, this );
+			}
+		});
+	},
+
+	after: function() {
+		return this.domManip( arguments, function( elem ) {
+			if ( this.parentNode ) {
+				this.parentNode.insertBefore( elem, this.nextSibling );
+			}
+		});
+	},
+
+	remove: function( selector, keepData /* Internal Use Only */ ) {
+		var elem,
+			elems = selector ? jQuery.filter( selector, this ) : this,
+			i = 0;
+
+		for ( ; (elem = elems[i]) != null; i++ ) {
+			if ( !keepData && elem.nodeType === 1 ) {
+				jQuery.cleanData( getAll( elem ) );
+			}
+
+			if ( elem.parentNode ) {
+				if ( keepData && jQuery.contains( elem.ownerDocument, elem ) ) {
+					setGlobalEval( getAll( elem, "script" ) );
+				}
+				elem.parentNode.removeChild( elem );
+			}
+		}
+
+		return this;
+	},
+
+	empty: function() {
+		var elem,
+			i = 0;
+
+		for ( ; (elem = this[i]) != null; i++ ) {
+			if ( elem.nodeType === 1 ) {
+
+				// Prevent memory leaks
+				jQuery.cleanData( getAll( elem, false ) );
+
+				// Remove any remaining nodes
+				elem.textContent = "";
+			}
+		}
+
+		return this;
+	},
+
+	clone: function( dataAndEvents, deepDataAndEvents ) {
+		dataAndEvents = dataAndEvents == null ? false : dataAndEvents;
+		deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents;
+
+		return this.map(function() {
+			return jQuery.clone( this, dataAndEvents, deepDataAndEvents );
+		});
+	},
+
+	html: function( value ) {
+		return access( this, function( value ) {
+			var elem = this[ 0 ] || {},
+				i = 0,
+				l = this.length;
+
+			if ( value === undefined && elem.nodeType === 1 ) {
+				return elem.innerHTML;
+			}
+
+			// See if we can take a shortcut and just use innerHTML
+			if ( typeof value === "string" && !rnoInnerhtml.test( value ) &&
+				!wrapMap[ ( rtagName.exec( value ) || [ "", "" ] )[ 1 ].toLowerCase() ] ) {
+
+				value = value.replace( rxhtmlTag, "<$1></$2>" );
+
+				try {
+					for ( ; i < l; i++ ) {
+						elem = this[ i ] || {};
+
+						// Remove element nodes and prevent memory leaks
+						if ( elem.nodeType === 1 ) {
+							jQuery.cleanData( getAll( elem, false ) );
+							elem.innerHTML = value;
+						}
+					}
+
+					elem = 0;
+
+				// If using innerHTML throws an exception, use the fallback method
+				} catch( e ) {}
+			}
+
+			if ( elem ) {
+				this.empty().append( value );
+			}
+		}, null, value, arguments.length );
+	},
+
+	replaceWith: function() {
+		var arg = arguments[ 0 ];
+
+		// Make the changes, replacing each context element with the new content
+		this.domManip( arguments, function( elem ) {
+			arg = this.parentNode;
+
+			jQuery.cleanData( getAll( this ) );
+
+			if ( arg ) {
+				arg.replaceChild( elem, this );
+			}
+		});
+
+		// Force removal if there was no new content (e.g., from empty arguments)
+		return arg && (arg.length || arg.nodeType) ? this : this.remove();
+	},
+
+	detach: function( selector ) {
+		return this.remove( selector, true );
+	},
+
+	domManip: function( args, callback ) {
+
+		// Flatten any nested arrays
+		args = concat.apply( [], args );
+
+		var fragment, first, scripts, hasScripts, node, doc,
+			i = 0,
+			l = this.length,
+			set = this,
+			iNoClone = l - 1,
+			value = args[ 0 ],
+			isFunction = jQuery.isFunction( value );
+
+		// We can't cloneNode fragments that contain checked, in WebKit
+		if ( isFunction ||
+				( l > 1 && typeof value === "string" &&
+					!support.checkClone && rchecked.test( value ) ) ) {
+			return this.each(function( index ) {
+				var self = set.eq( index );
+				if ( isFunction ) {
+					args[ 0 ] = value.call( this, index, self.html() );
+				}
+				self.domManip( args, callback );
+			});
+		}
+
+		if ( l ) {
+			fragment = jQuery.buildFragment( args, this[ 0 ].ownerDocument, false, this );
+			first = fragment.firstChild;
+
+			if ( fragment.childNodes.length === 1 ) {
+				fragment = first;
+			}
+
+			if ( first ) {
+				scripts = jQuery.map( getAll( fragment, "script" ), disableScript );
+				hasScripts = scripts.length;
+
+				// Use the original fragment for the last item instead of the first because it can end up
+				// being emptied incorrectly in certain situations (#8070).
+				for ( ; i < l; i++ ) {
+					node = fragment;
+
+					if ( i !== iNoClone ) {
+						node = jQuery.clone( node, true, true );
+
+						// Keep references to cloned scripts for later restoration
+						if ( hasScripts ) {
+							// Support: QtWebKit
+							// jQuery.merge because push.apply(_, arraylike) throws
+							jQuery.merge( scripts, getAll( node, "script" ) );
+						}
+					}
+
+					callback.call( this[ i ], node, i );
+				}
+
+				if ( hasScripts ) {
+					doc = scripts[ scripts.length - 1 ].ownerDocument;
+
+					// Reenable scripts
+					jQuery.map( scripts, restoreScript );
+
+					// Evaluate executable scripts on first document insertion
+					for ( i = 0; i < hasScripts; i++ ) {
+						node = scripts[ i ];
+						if ( rscriptType.test( node.type || "" ) &&
+							!data_priv.access( node, "globalEval" ) && jQuery.contains( doc, node ) ) {
+
+							if ( node.src ) {
+								// Optional AJAX dependency, but won't run scripts if not present
+								if ( jQuery._evalUrl ) {
+									jQuery._evalUrl( node.src );
+								}
+							} else {
+								jQuery.globalEval( node.textContent.replace( rcleanScript, "" ) );
+							}
+						}
+					}
+				}
+			}
+		}
+
+		return this;
+	}
+});
+
+jQuery.each({
+	appendTo: "append",
+	prependTo: "prepend",
+	insertBefore: "before",
+	insertAfter: "after",
+	replaceAll: "replaceWith"
+}, function( name, original ) {
+	jQuery.fn[ name ] = function( selector ) {
+		var elems,
+			ret = [],
+			insert = jQuery( selector ),
+			last = insert.length - 1,
+			i = 0;
+
+		for ( ; i <= last; i++ ) {
+			elems = i === last ? this : this.clone( true );
+			jQuery( insert[ i ] )[ original ]( elems );
+
+			// Support: QtWebKit
+			// .get() because push.apply(_, arraylike) throws
+			push.apply( ret, elems.get() );
+		}
+
+		return this.pushStack( ret );
+	};
+});
+
+
+var iframe,
+	elemdisplay = {};
+
+/**
+ * Retrieve the actual display of a element
+ * @param {String} name nodeName of the element
+ * @param {Object} doc Document object
+ */
+// Called only from within defaultDisplay
+function actualDisplay( name, doc ) {
+	var style,
+		elem = jQuery( doc.createElement( name ) ).appendTo( doc.body ),
+
+		// getDefaultComputedStyle might be reliably used only on attached element
+		display = window.getDefaultComputedStyle && ( style = window.getDefaultComputedStyle( elem[ 0 ] ) ) ?
+
+			// Use of this method is a temporary fix (more like optmization) until something better comes along,
+			// since it was removed from specification and supported only in FF
+			style.display : jQuery.css( elem[ 0 ], "display" );
+
+	// We don't have any data stored on the element,
+	// so use "detach" method as fast way to get rid of the element
+	elem.detach();
+
+	return display;
+}
+
+/**
+ * Try to determine the default display value of an element
+ * @param {String} nodeName
+ */
+function defaultDisplay( nodeName ) {
+	var doc = document,
+		display = elemdisplay[ nodeName ];
+
+	if ( !display ) {
+		display = actualDisplay( nodeName, doc );
+
+		// If the simple way fails, read from inside an iframe
+		if ( display === "none" || !display ) {
+
+			// Use the already-created iframe if possible
+			iframe = (iframe || jQuery( "<iframe frameborder='0' width='0' height='0'/>" )).appendTo( doc.documentElement );
+
+			// Always write a new HTML skeleton so Webkit and Firefox don't choke on reuse
+			doc = iframe[ 0 ].contentDocument;
+
+			// Support: IE
+			doc.write();
+			doc.close();
+
+			display = actualDisplay( nodeName, doc );
+			iframe.detach();
+		}
+
+		// Store the correct default display
+		elemdisplay[ nodeName ] = display;
+	}
+
+	return display;
+}
+var rmargin = (/^margin/);
+
+var rnumnonpx = new RegExp( "^(" + pnum + ")(?!px)[a-z%]+$", "i" );
+
+var getStyles = function( elem ) {
+		return elem.ownerDocument.defaultView.getComputedStyle( elem, null );
+	};
+
+
+
+function curCSS( elem, name, computed ) {
+	var width, minWidth, maxWidth, ret,
+		style = elem.style;
+
+	computed = computed || getStyles( elem );
+
+	// Support: IE9
+	// getPropertyValue is only needed for .css('filter') in IE9, see #12537
+	if ( computed ) {
+		ret = computed.getPropertyValue( name ) || computed[ name ];
+	}
+
+	if ( computed ) {
+
+		if ( ret === "" && !jQuery.contains( elem.ownerDocument, elem ) ) {
+			ret = jQuery.style( elem, name );
+		}
+
+		// Support: iOS < 6
+		// A tribute to the "awesome hack by Dean Edwards"
+		// iOS < 6 (at least) returns percentage for a larger set of values, but width seems to be reliably pixels
+		// this is against the CSSOM draft spec: http://dev.w3.org/csswg/cssom/#resolved-values
+		if ( rnumnonpx.test( ret ) && rmargin.test( name ) ) {
+
+			// Remember the original values
+			width = style.width;
+			minWidth = style.minWidth;
+			maxWidth = style.maxWidth;
+
+			// Put in the new values to get a computed value out
+			style.minWidth = style.maxWidth = style.width = ret;
+			ret = computed.width;
+
+			// Revert the changed values
+			style.width = width;
+			style.minWidth = minWidth;
+			style.maxWidth = maxWidth;
+		}
+	}
+
+	return ret !== undefined ?
+		// Support: IE
+		// IE returns zIndex value as an integer.
+		ret + "" :
+		ret;
+}
+
+
+function addGetHookIf( conditionFn, hookFn ) {
+	// Define the hook, we'll check on the first run if it's really needed.
+	return {
+		get: function() {
+			if ( conditionFn() ) {
+				// Hook not needed (or it's not possible to use it due to missing dependency),
+				// remove it.
+				// Since there are no other hooks for marginRight, remove the whole object.
+				delete this.get;
+				return;
+			}
+
+			// Hook needed; redefine it so that the support test is not executed again.
+
+			return (this.get = hookFn).apply( this, arguments );
+		}
+	};
+}
+
+
+(function() {
+	var pixelPositionVal, boxSizingReliableVal,
+		docElem = document.documentElement,
+		container = document.createElement( "div" ),
+		div = document.createElement( "div" );
+
+	if ( !div.style ) {
+		return;
+	}
+
+	div.style.backgroundClip = "content-box";
+	div.cloneNode( true ).style.backgroundClip = "";
+	support.clearCloneStyle = div.style.backgroundClip === "content-box";
+
+	container.style.cssText = "border:0;width:0;height:0;top:0;left:-9999px;margin-top:1px;" +
+		"position:absolute";
+	container.appendChild( div );
+
+	// Executing both pixelPosition & boxSizingReliable tests require only one layout
+	// so they're executed at the same time to save the second computation.
+	function computePixelPositionAndBoxSizingReliable() {
+		div.style.cssText =
+			// Support: Firefox<29, Android 2.3
+			// Vendor-prefix box-sizing
+			"-webkit-box-sizing:border-box;-moz-box-sizing:border-box;" +
+			"box-sizing:border-box;display:block;margin-top:1%;top:1%;" +
+			"border:1px;padding:1px;width:4px;position:absolute";
+		div.innerHTML = "";
+		docElem.appendChild( container );
+
+		var divStyle = window.getComputedStyle( div, null );
+		pixelPositionVal = divStyle.top !== "1%";
+		boxSizingReliableVal = divStyle.width === "4px";
+
+		docElem.removeChild( container );
+	}
+
+	// Support: node.js jsdom
+	// Don't assume that getComputedStyle is a property of the global object
+	if ( window.getComputedStyle ) {
+		jQuery.extend( support, {
+			pixelPosition: function() {
+				// This test is executed only once but we still do memoizing
+				// since we can use the boxSizingReliable pre-computing.
+				// No need to check if the test was already performed, though.
+				computePixelPositionAndBoxSizingReliable();
+				return pixelPositionVal;
+			},
+			boxSizingReliable: function() {
+				if ( boxSizingReliableVal == null ) {
+					computePixelPositionAndBoxSizingReliable();
+				}
+				return boxSizingReliableVal;
+			},
+			reliableMarginRight: function() {
+				// Support: Android 2.3
+				// Check if div with explicit width and no margin-right incorrectly
+				// gets computed margin-right based on width of container. (#3333)
+				// WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right
+				// This support function is only executed once so no memoizing is needed.
+				var ret,
+					marginDiv = div.appendChild( document.createElement( "div" ) );
+
+				// Reset CSS: box-sizing; display; margin; border; padding
+				marginDiv.style.cssText = div.style.cssText =
+					// Support: Firefox<29, Android 2.3
+					// Vendor-prefix box-sizing
+					"-webkit-box-sizing:content-box;-moz-box-sizing:content-box;" +
+					"box-sizing:content-box;display:block;margin:0;border:0;padding:0";
+				marginDiv.style.marginRight = marginDiv.style.width = "0";
+				div.style.width = "1px";
+				docElem.appendChild( container );
+
+				ret = !parseFloat( window.getComputedStyle( marginDiv, null ).marginRight );
+
+				docElem.removeChild( container );
+
+				return ret;
+			}
+		});
+	}
+})();
+
+
+// A method for quickly swapping in/out CSS properties to get correct calculations.
+jQuery.swap = function( elem, options, callback, args ) {
+	var ret, name,
+		old = {};
+
+	// Remember the old values, and insert the new ones
+	for ( name in options ) {
+		old[ name ] = elem.style[ name ];
+		elem.style[ name ] = options[ name ];
+	}
+
+	ret = callback.apply( elem, args || [] );
+
+	// Revert the old values
+	for ( name in options ) {
+		elem.style[ name ] = old[ name ];
+	}
+
+	return ret;
+};
+
+
+var
+	// swappable if display is none or starts with table except "table", "table-cell", or "table-caption"
+	// see here for display values: https://developer.mozilla.org/en-US/docs/CSS/display
+	rdisplayswap = /^(none|table(?!-c[ea]).+)/,
+	rnumsplit = new RegExp( "^(" + pnum + ")(.*)$", "i" ),
+	rrelNum = new RegExp( "^([+-])=(" + pnum + ")", "i" ),
+
+	cssShow = { position: "absolute", visibility: "hidden", display: "block" },
+	cssNormalTransform = {
+		letterSpacing: "0",
+		fontWeight: "400"
+	},
+
+	cssPrefixes = [ "Webkit", "O", "Moz", "ms" ];
+
+// return a css property mapped to a potentially vendor prefixed property
+function vendorPropName( style, name ) {
+
+	// shortcut for names that are not vendor prefixed
+	if ( name in style ) {
+		return name;
+	}
+
+	// check for vendor prefixed names
+	var capName = name[0].toUpperCase() + name.slice(1),
+		origName = name,
+		i = cssPrefixes.length;
+
+	while ( i-- ) {
+		name = cssPrefixes[ i ] + capName;
+		if ( name in style ) {
+			return name;
+		}
+	}
+
+	return origName;
+}
+
+function setPositiveNumber( elem, value, subtract ) {
+	var matches = rnumsplit.exec( value );
+	return matches ?
+		// Guard against undefined "subtract", e.g., when used as in cssHooks
+		Math.max( 0, matches[ 1 ] - ( subtract || 0 ) ) + ( matches[ 2 ] || "px" ) :
+		value;
+}
+
+function augmentWidthOrHeight( elem, name, extra, isBorderBox, styles ) {
+	var i = extra === ( isBorderBox ? "border" : "content" ) ?
+		// If we already have the right measurement, avoid augmentation
+		4 :
+		// Otherwise initialize for horizontal or vertical properties
+		name === "width" ? 1 : 0,
+
+		val = 0;
+
+	for ( ; i < 4; i += 2 ) {
+		// both box models exclude margin, so add it if we want it
+		if ( extra === "margin" ) {
+			val += jQuery.css( elem, extra + cssExpand[ i ], true, styles );
+		}
+
+		if ( isBorderBox ) {
+			// border-box includes padding, so remove it if we want content
+			if ( extra === "content" ) {
+				val -= jQuery.css( elem, "padding" + cssExpand[ i ], true, styles );
+			}
+
+			// at this point, extra isn't border nor margin, so remove border
+			if ( extra !== "margin" ) {
+				val -= jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles );
+			}
+		} else {
+			// at this point, extra isn't content, so add padding
+			val += jQuery.css( elem, "padding" + cssExpand[ i ], true, styles );
+
+			// at this point, extra isn't content nor padding, so add border
+			if ( extra !== "padding" ) {
+				val += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles );
+			}
+		}
+	}
+
+	return val;
+}
+
+function getWidthOrHeight( elem, name, extra ) {
+
+	// Start with offset property, which is equivalent to the border-box value
+	var valueIsBorderBox = true,
+		val = name === "width" ? elem.offsetWidth : elem.offsetHeight,
+		styles = getStyles( elem ),
+		isBorderBox = jQuery.css( elem, "boxSizing", false, styles ) === "border-box";
+
+	// some non-html elements return undefined for offsetWidth, so check for null/undefined
+	// svg - https://bugzilla.mozilla.org/show_bug.cgi?id=649285
+	// MathML - https://bugzilla.mozilla.org/show_bug.cgi?id=491668
+	if ( val <= 0 || val == null ) {
+		// Fall back to computed then uncomputed css if necessary
+		val = curCSS( elem, name, styles );
+		if ( val < 0 || val == null ) {
+			val = elem.style[ name ];
+		}
+
+		// Computed unit is not pixels. Stop here and return.
+		if ( rnumnonpx.test(val) ) {
+			return val;
+		}
+
+		// we need the check for style in case a browser which returns unreliable values
+		// for getComputedStyle silently falls back to the reliable elem.style
+		valueIsBorderBox = isBorderBox &&
+			( support.boxSizingReliable() || val === elem.style[ name ] );
+
+		// Normalize "", auto, and prepare for extra
+		val = parseFloat( val ) || 0;
+	}
+
+	// use the active box-sizing model to add/subtract irrelevant styles
+	return ( val +
+		augmentWidthOrHeight(
+			elem,
+			name,
+			extra || ( isBorderBox ? "border" : "content" ),
+			valueIsBorderBox,
+			styles
+		)
+	) + "px";
+}
+
+function showHide( elements, show ) {
+	var display, elem, hidden,
+		values = [],
+		index = 0,
+		length = elements.length;
+
+	for ( ; index < length; index++ ) {
+		elem = elements[ index ];
+		if ( !elem.style ) {
+			continue;
+		}
+
+		values[ index ] = data_priv.get( elem, "olddisplay" );
+		display = elem.style.display;
+		if ( show ) {
+			// Reset the inline display of this element to learn if it is
+			// being hidden by cascaded rules or not
+			if ( !values[ index ] && display === "none" ) {
+				elem.style.display = "";
+			}
+
+			// Set elements which have been overridden with display: none
+			// in a stylesheet to whatever the default browser style is
+			// for such an element
+			if ( elem.style.display === "" && isHidden( elem ) ) {
+				values[ index ] = data_priv.access( elem, "olddisplay", defaultDisplay(elem.nodeName) );
+			}
+		} else {
+			hidden = isHidden( elem );
+
+			if ( display !== "none" || !hidden ) {
+				data_priv.set( elem, "olddisplay", hidden ? display : jQuery.css( elem, "display" ) );
+			}
+		}
+	}
+
+	// Set the display of most of the elements in a second loop
+	// to avoid the constant reflow
+	for ( index = 0; index < length; index++ ) {
+		elem = elements[ index ];
+		if ( !elem.style ) {
+			continue;
+		}
+		if ( !show || elem.style.display === "none" || elem.style.display === "" ) {
+			elem.style.display = show ? values[ index ] || "" : "none";
+		}
+	}
+
+	return elements;
+}
+
+jQuery.extend({
+	// Add in style property hooks for overriding the default
+	// behavior of getting and setting a style property
+	cssHooks: {
+		opacity: {
+			get: function( elem, computed ) {
+				if ( computed ) {
+					// We should always get a number back from opacity
+					var ret = curCSS( elem, "opacity" );
+					return ret === "" ? "1" : ret;
+				}
+			}
+		}
+	},
+
+	// Don't automatically add "px" to these possibly-unitless properties
+	cssNumber: {
+		"columnCount": true,
+		"fillOpacity": true,
+		"flexGrow": true,
+		"flexShrink": true,
+		"fontWeight": true,
+		"lineHeight": true,
+		"opacity": true,
+		"order": true,
+		"orphans": true,
+		"widows": true,
+		"zIndex": true,
+		"zoom": true
+	},
+
+	// Add in properties whose names you wish to fix before
+	// setting or getting the value
+	cssProps: {
+		// normalize float css property
+		"float": "cssFloat"
+	},
+
+	// Get and set the style property on a DOM Node
+	style: function( elem, name, value, extra ) {
+		// Don't set styles on text and comment nodes
+		if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) {
+			return;
+		}
+
+		// Make sure that we're working with the right name
+		var ret, type, hooks,
+			origName = jQuery.camelCase( name ),
+			style = elem.style;
+
+		name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( style, origName ) );
+
+		// gets hook for the prefixed version
+		// followed by the unprefixed version
+		hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ];
+
+		// Check if we're setting a value
+		if ( value !== undefined ) {
+			type = typeof value;
+
+			// convert relative number strings (+= or -=) to relative numbers. #7345
+			if ( type === "string" && (ret = rrelNum.exec( value )) ) {
+				value = ( ret[1] + 1 ) * ret[2] + parseFloat( jQuery.css( elem, name ) );
+				// Fixes bug #9237
+				type = "number";
+			}
+
+			// Make sure that null and NaN values aren't set. See: #7116
+			if ( value == null || value !== value ) {
+				return;
+			}
+
+			// If a number was passed in, add 'px' to the (except for certain CSS properties)
+			if ( type === "number" && !jQuery.cssNumber[ origName ] ) {
+				value += "px";
+			}
+
+			// Fixes #8908, it can be done more correctly by specifying setters in cssHooks,
+			// but it would mean to define eight (for every problematic property) identical functions
+			if ( !support.clearCloneStyle && value === "" && name.indexOf( "background" ) === 0 ) {
+				style[ name ] = "inherit";
+			}
+
+			// If a hook was provided, use that value, otherwise just set the specified value
+			if ( !hooks || !("set" in hooks) || (value = hooks.set( elem, value, extra )) !== undefined ) {
+				style[ name ] = value;
+			}
+
+		} else {
+			// If a hook was provided get the non-computed value from there
+			if ( hooks && "get" in hooks && (ret = hooks.get( elem, false, extra )) !== undefined ) {
+				return ret;
+			}
+
+			// Otherwise just get the value from the style object
+			return style[ name ];
+		}
+	},
+
+	css: function( elem, name, extra, styles ) {
+		var val, num, hooks,
+			origName = jQuery.camelCase( name );
+
+		// Make sure that we're working with the right name
+		name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( elem.style, origName ) );
+
+		// gets hook for the prefixed version
+		// followed by the unprefixed version
+		hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ];
+
+		// If a hook was provided get the computed value from there
+		if ( hooks && "get" in hooks ) {
+			val = hooks.get( elem, true, extra );
+		}
+
+		// Otherwise, if a way to get the computed value exists, use that
+		if ( val === undefined ) {
+			val = curCSS( elem, name, styles );
+		}
+
+		//convert "normal" to computed value
+		if ( val === "normal" && name in cssNormalTransform ) {
+			val = cssNormalTransform[ name ];
+		}
+
+		// Return, converting to number if forced or a qualifier was provided and val looks numeric
+		if ( extra === "" || extra ) {
+			num = parseFloat( val );
+			return extra === true || jQuery.isNumeric( num ) ? num || 0 : val;
+		}
+		return val;
+	}
+});
+
+jQuery.each([ "height", "width" ], function( i, name ) {
+	jQuery.cssHooks[ name ] = {
+		get: function( elem, computed, extra ) {
+			if ( computed ) {
+				// certain elements can have dimension info if we invisibly show them
+				// however, it must have a current display style that would benefit from this
+				return rdisplayswap.test( jQuery.css( elem, "display" ) ) && elem.offsetWidth === 0 ?
+					jQuery.swap( elem, cssShow, function() {
+						return getWidthOrHeight( elem, name, extra );
+					}) :
+					getWidthOrHeight( elem, name, extra );
+			}
+		},
+
+		set: function( elem, value, extra ) {
+			var styles = extra && getStyles( elem );
+			return setPositiveNumber( elem, value, extra ?
+				augmentWidthOrHeight(
+					elem,
+					name,
+					extra,
+					jQuery.css( elem, "boxSizing", false, styles ) === "border-box",
+					styles
+				) : 0
+			);
+		}
+	};
+});
+
+// Support: Android 2.3
+jQuery.cssHooks.marginRight = addGetHookIf( support.reliableMarginRight,
+	function( elem, computed ) {
+		if ( computed ) {
+			// WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right
+			// Work around by temporarily setting element display to inline-block
+			return jQuery.swap( elem, { "display": "inline-block" },
+				curCSS, [ elem, "marginRight" ] );
+		}
+	}
+);
+
+// These hooks are used by animate to expand properties
+jQuery.each({
+	margin: "",
+	padding: "",
+	border: "Width"
+}, function( prefix, suffix ) {
+	jQuery.cssHooks[ prefix + suffix ] = {
+		expand: function( value ) {
+			var i = 0,
+				expanded = {},
+
+				// assumes a single number if not a string
+				parts = typeof value === "string" ? value.split(" ") : [ value ];
+
+			for ( ; i < 4; i++ ) {
+				expanded[ prefix + cssExpand[ i ] + suffix ] =
+					parts[ i ] || parts[ i - 2 ] || parts[ 0 ];
+			}
+
+			return expanded;
+		}
+	};
+
+	if ( !rmargin.test( prefix ) ) {
+		jQuery.cssHooks[ prefix + suffix ].set = setPositiveNumber;
+	}
+});
+
+jQuery.fn.extend({
+	css: function( name, value ) {
+		return access( this, function( elem, name, value ) {
+			var styles, len,
+				map = {},
+				i = 0;
+
+			if ( jQuery.isArray( name ) ) {
+				styles = getStyles( elem );
+				len = name.length;
+
+				for ( ; i < len; i++ ) {
+					map[ name[ i ] ] = jQuery.css( elem, name[ i ], false, styles );
+				}
+
+				return map;
+			}
+
+			return value !== undefined ?
+				jQuery.style( elem, name, value ) :
+				jQuery.css( elem, name );
+		}, name, value, arguments.length > 1 );
+	},
+	show: function() {
+		return showHide( this, true );
+	},
+	hide: function() {
+		return showHide( this );
+	},
+	toggle: function( state ) {
+		if ( typeof state === "boolean" ) {
+			return state ? this.show() : this.hide();
+		}
+
+		return this.each(function() {
+			if ( isHidden( this ) ) {
+				jQuery( this ).show();
+			} else {
+				jQuery( this ).hide();
+			}
+		});
+	}
+});
+
+
+function Tween( elem, options, prop, end, easing ) {
+	return new Tween.prototype.init( elem, options, prop, end, easing );
+}
+jQuery.Tween = Tween;
+
+Tween.prototype = {
+	constructor: Tween,
+	init: function( elem, options, prop, end, easing, unit ) {
+		this.elem = elem;
+		this.prop = prop;
+		this.easing = easing || "swing";
+		this.options = options;
+		this.start = this.now = this.cur();
+		this.end = end;
+		this.unit = unit || ( jQuery.cssNumber[ prop ] ? "" : "px" );
+	},
+	cur: function() {
+		var hooks = Tween.propHooks[ this.prop ];
+
+		return hooks && hooks.get ?
+			hooks.get( this ) :
+			Tween.propHooks._default.get( this );
+	},
+	run: function( percent ) {
+		var eased,
+			hooks = Tween.propHooks[ this.prop ];
+
+		if ( this.options.duration ) {
+			this.pos = eased = jQuery.easing[ this.easing ](
+				percent, this.options.duration * percent, 0, 1, this.options.duration
+			);
+		} else {
+			this.pos = eased = percent;
+		}
+		this.now = ( this.end - this.start ) * eased + this.start;
+
+		if ( this.options.step ) {
+			this.options.step.call( this.elem, this.now, this );
+		}
+
+		if ( hooks && hooks.set ) {
+			hooks.set( this );
+		} else {
+			Tween.propHooks._default.set( this );
+		}
+		return this;
+	}
+};
+
+Tween.prototype.init.prototype = Tween.prototype;
+
+Tween.propHooks = {
+	_default: {
+		get: function( tween ) {
+			var result;
+
+			if ( tween.elem[ tween.prop ] != null &&
+				(!tween.elem.style || tween.elem.style[ tween.prop ] == null) ) {
+				return tween.elem[ tween.prop ];
+			}
+
+			// passing an empty string as a 3rd parameter to .css will automatically
+			// attempt a parseFloat and fallback to a string if the parse fails
+			// so, simple values such as "10px" are parsed to Float.
+			// complex values such as "rotate(1rad)" are returned as is.
+			result = jQuery.css( tween.elem, tween.prop, "" );
+			// Empty strings, null, undefined and "auto" are converted to 0.
+			return !result || result === "auto" ? 0 : result;
+		},
+		set: function( tween ) {
+			// use step hook for back compat - use cssHook if its there - use .style if its
+			// available and use plain properties where available
+			if ( jQuery.fx.step[ tween.prop ] ) {
+				jQuery.fx.step[ tween.prop ]( tween );
+			} else if ( tween.elem.style && ( tween.elem.style[ jQuery.cssProps[ tween.prop ] ] != null || jQuery.cssHooks[ tween.prop ] ) ) {
+				jQuery.style( tween.elem, tween.prop, tween.now + tween.unit );
+			} else {
+				tween.elem[ tween.prop ] = tween.now;
+			}
+		}
+	}
+};
+
+// Support: IE9
+// Panic based approach to setting things on disconnected nodes
+
+Tween.propHooks.scrollTop = Tween.propHooks.scrollLeft = {
+	set: function( tween ) {
+		if ( tween.elem.nodeType && tween.elem.parentNode ) {
+			tween.elem[ tween.prop ] = tween.now;
+		}
+	}
+};
+
+jQuery.easing = {
+	linear: function( p ) {
+		return p;
+	},
+	swing: function( p ) {
+		return 0.5 - Math.cos( p * Math.PI ) / 2;
+	}
+};
+
+jQuery.fx = Tween.prototype.init;
+
+// Back Compat <1.8 extension point
+jQuery.fx.step = {};
+
+
+
+
+var
+	fxNow, timerId,
+	rfxtypes = /^(?:toggle|show|hide)$/,
+	rfxnum = new RegExp( "^(?:([+-])=|)(" + pnum + ")([a-z%]*)$", "i" ),
+	rrun = /queueHooks$/,
+	animationPrefilters = [ defaultPrefilter ],
+	tweeners = {
+		"*": [ function( prop, value ) {
+			var tween = this.createTween( prop, value ),
+				target = tween.cur(),
+				parts = rfxnum.exec( value ),
+				unit = parts && parts[ 3 ] || ( jQuery.cssNumber[ prop ] ? "" : "px" ),
+
+				// Starting value computation is required for potential unit mismatches
+				start = ( jQuery.cssNumber[ prop ] || unit !== "px" && +target ) &&
+					rfxnum.exec( jQuery.css( tween.elem, prop ) ),
+				scale = 1,
+				maxIterations = 20;
+
+			if ( start && start[ 3 ] !== unit ) {
+				// Trust units reported by jQuery.css
+				unit = unit || start[ 3 ];
+
+				// Make sure we update the tween properties later on
+				parts = parts || [];
+
+				// Iteratively approximate from a nonzero starting point
+				start = +target || 1;
+
+				do {
+					// If previous iteration zeroed out, double until we get *something*
+					// Use a string for doubling factor so we don't accidentally see scale as unchanged below
+					scale = scale || ".5";
+
+					// Adjust and apply
+					start = start / scale;
+					jQuery.style( tween.elem, prop, start + unit );
+
+				// Update scale, tolerating zero or NaN from tween.cur()
+				// And breaking the loop if scale is unchanged or perfect, or if we've just had enough
+				} while ( scale !== (scale = tween.cur() / target) && scale !== 1 && --maxIterations );
+			}
+
+			// Update tween properties
+			if ( parts ) {
+				start = tween.start = +start || +target || 0;
+				tween.unit = unit;
+				// If a +=/-= token was provided, we're doing a relative animation
+				tween.end = parts[ 1 ] ?
+					start + ( parts[ 1 ] + 1 ) * parts[ 2 ] :
+					+parts[ 2 ];
+			}
+
+			return tween;
+		} ]
+	};
+
+// Animations created synchronously will run synchronously
+function createFxNow() {
+	setTimeout(function() {
+		fxNow = undefined;
+	});
+	return ( fxNow = jQuery.now() );
+}
+
+// Generate parameters to create a standard animation
+function genFx( type, includeWidth ) {
+	var which,
+		i = 0,
+		attrs = { height: type };
+
+	// if we include width, step value is 1 to do all cssExpand values,
+	// if we don't include width, step value is 2 to skip over Left and Right
+	includeWidth = includeWidth ? 1 : 0;
+	for ( ; i < 4 ; i += 2 - includeWidth ) {
+		which = cssExpand[ i ];
+		attrs[ "margin" + which ] = attrs[ "padding" + which ] = type;
+	}
+
+	if ( includeWidth ) {
+		attrs.opacity = attrs.width = type;
+	}
+
+	return attrs;
+}
+
+function createTween( value, prop, animation ) {
+	var tween,
+		collection = ( tweeners[ prop ] || [] ).concat( tweeners[ "*" ] ),
+		index = 0,
+		length = collection.length;
+	for ( ; index < length; index++ ) {
+		if ( (tween = collection[ index ].call( animation, prop, value )) ) {
+
+			// we're done with this property
+			return tween;
+		}
+	}
+}
+
+function defaultPrefilter( elem, props, opts ) {
+	/* jshint validthis: true */
+	var prop, value, toggle, tween, hooks, oldfire, display, checkDisplay,
+		anim = this,
+		orig = {},
+		style = elem.style,
+		hidden = elem.nodeType && isHidden( elem ),
+		dataShow = data_priv.get( elem, "fxshow" );
+
+	// handle queue: false promises
+	if ( !opts.queue ) {
+		hooks = jQuery._queueHooks( elem, "fx" );
+		if ( hooks.unqueued == null ) {
+			hooks.unqueued = 0;
+			oldfire = hooks.empty.fire;
+			hooks.empty.fire = function() {
+				if ( !hooks.unqueued ) {
+					oldfire();
+				}
+			};
+		}
+		hooks.unqueued++;
+
+		anim.always(function() {
+			// doing this makes sure that the complete handler will be called
+			// before this completes
+			anim.always(function() {
+				hooks.unqueued--;
+				if ( !jQuery.queue( elem, "fx" ).length ) {
+					hooks.empty.fire();
+				}
+			});
+		});
+	}
+
+	// height/width overflow pass
+	if ( elem.nodeType === 1 && ( "height" in props || "width" in props ) ) {
+		// Make sure that nothing sneaks out
+		// Record all 3 overflow attributes because IE9-10 do not
+		// change the overflow attribute when overflowX and
+		// overflowY are set to the same value
+		opts.overflow = [ style.overflow, style.overflowX, style.overflowY ];
+
+		// Set display property to inline-block for height/width
+		// animations on inline elements that are having width/height animated
+		display = jQuery.css( elem, "display" );
+
+		// Test default display if display is currently "none"
+		checkDisplay = display === "none" ?
+			data_priv.get( elem, "olddisplay" ) || defaultDisplay( elem.nodeName ) : display;
+
+		if ( checkDisplay === "inline" && jQuery.css( elem, "float" ) === "none" ) {
+			style.display = "inline-block";
+		}
+	}
+
+	if ( opts.overflow ) {
+		style.overflow = "hidden";
+		anim.always(function() {
+			style.overflow = opts.overflow[ 0 ];
+			style.overflowX = opts.overflow[ 1 ];
+			style.overflowY = opts.overflow[ 2 ];
+		});
+	}
+
+	// show/hide pass
+	for ( prop in props ) {
+		value = props[ prop ];
+		if ( rfxtypes.exec( value ) ) {
+			delete props[ prop ];
+			toggle = toggle || value === "toggle";
+			if ( value === ( hidden ? "hide" : "show" ) ) {
+
+				// If there is dataShow left over from a stopped hide or show and we are going to proceed with show, we should pretend to be hidden
+				if ( value === "show" && dataShow && dataShow[ prop ] !== undefined ) {
+					hidden = true;
+				} else {
+					continue;
+				}
+			}
+			orig[ prop ] = dataShow && dataShow[ prop ] || jQuery.style( elem, prop );
+
+		// Any non-fx value stops us from restoring the original display value
+		} else {
+			display = undefined;
+		}
+	}
+
+	if ( !jQuery.isEmptyObject( orig ) ) {
+		if ( dataShow ) {
+			if ( "hidden" in dataShow ) {
+				hidden = dataShow.hidden;
+			}
+		} else {
+			dataShow = data_priv.access( elem, "fxshow", {} );
+		}
+
+		// store state if its toggle - enables .stop().toggle() to "reverse"
+		if ( toggle ) {
+			dataShow.hidden = !hidden;
+		}
+		if ( hidden ) {
+			jQuery( elem ).show();
+		} else {
+			anim.done(function() {
+				jQuery( elem ).hide();
+			});
+		}
+		anim.done(function() {
+			var prop;
+
+			data_priv.remove( elem, "fxshow" );
+			for ( prop in orig ) {
+				jQuery.style( elem, prop, orig[ prop ] );
+			}
+		});
+		for ( prop in orig ) {
+			tween = createTween( hidden ? dataShow[ prop ] : 0, prop, anim );
+
+			if ( !( prop in dataShow ) ) {
+				dataShow[ prop ] = tween.start;
+				if ( hidden ) {
+					tween.end = tween.start;
+					tween.start = prop === "width" || prop === "height" ? 1 : 0;
+				}
+			}
+		}
+
+	// If this is a noop like .hide().hide(), restore an overwritten display value
+	} else if ( (display === "none" ? defaultDisplay( elem.nodeName ) : display) === "inline" ) {
+		style.display = display;
+	}
+}
+
+function propFilter( props, specialEasing ) {
+	var index, name, easing, value, hooks;
+
+	// camelCase, specialEasing and expand cssHook pass
+	for ( index in props ) {
+		name = jQuery.camelCase( index );
+		easing = specialEasing[ name ];
+		value = props[ index ];
+		if ( jQuery.isArray( value ) ) {
+			easing = value[ 1 ];
+			value = props[ index ] = value[ 0 ];
+		}
+
+		if ( index !== name ) {
+			props[ name ] = value;
+			delete props[ index ];
+		}
+
+		hooks = jQuery.cssHooks[ name ];
+		if ( hooks && "expand" in hooks ) {
+			value = hooks.expand( value );
+			delete props[ name ];
+
+			// not quite $.extend, this wont overwrite keys already present.
+			// also - reusing 'index' from above because we have the correct "name"
+			for ( index in value ) {
+				if ( !( index in props ) ) {
+					props[ index ] = value[ index ];
+					specialEasing[ index ] = easing;
+				}
+			}
+		} else {
+			specialEasing[ name ] = easing;
+		}
+	}
+}
+
+function Animation( elem, properties, options ) {
+	var result,
+		stopped,
+		index = 0,
+		length = animationPrefilters.length,
+		deferred = jQuery.Deferred().always( function() {
+			// don't match elem in the :animated selector
+			delete tick.elem;
+		}),
+		tick = function() {
+			if ( stopped ) {
+				return false;
+			}
+			var currentTime = fxNow || createFxNow(),
+				remaining = Math.max( 0, animation.startTime + animation.duration - currentTime ),
+				// archaic crash bug won't allow us to use 1 - ( 0.5 || 0 ) (#12497)
+				temp = remaining / animation.duration || 0,
+				percent = 1 - temp,
+				index = 0,
+				length = animation.tweens.length;
+
+			for ( ; index < length ; index++ ) {
+				animation.tweens[ index ].run( percent );
+			}
+
+			deferred.notifyWith( elem, [ animation, percent, remaining ]);
+
+			if ( percent < 1 && length ) {
+				return remaining;
+			} else {
+				deferred.resolveWith( elem, [ animation ] );
+				return false;
+			}
+		},
+		animation = deferred.promise({
+			elem: elem,
+			props: jQuery.extend( {}, properties ),
+			opts: jQuery.extend( true, { specialEasing: {} }, options ),
+			originalProperties: properties,
+			originalOptions: options,
+			startTime: fxNow || createFxNow(),
+			duration: options.duration,
+			tweens: [],
+			createTween: function( prop, end ) {
+				var tween = jQuery.Tween( elem, animation.opts, prop, end,
+						animation.opts.specialEasing[ prop ] || animation.opts.easing );
+				animation.tweens.push( tween );
+				return tween;
+			},
+			stop: function( gotoEnd ) {
+				var index = 0,
+					// if we are going to the end, we want to run all the tweens
+					// otherwise we skip this part
+					length = gotoEnd ? animation.tweens.length : 0;
+				if ( stopped ) {
+					return this;
+				}
+				stopped = true;
+				for ( ; index < length ; index++ ) {
+					animation.tweens[ index ].run( 1 );
+				}
+
+				// resolve when we played the last frame
+				// otherwise, reject
+				if ( gotoEnd ) {
+					deferred.resolveWith( elem, [ animation, gotoEnd ] );
+				} else {
+					deferred.rejectWith( elem, [ animation, gotoEnd ] );
+				}
+				return this;
+			}
+		}),
+		props = animation.props;
+
+	propFilter( props, animation.opts.specialEasing );
+
+	for ( ; index < length ; index++ ) {
+		result = animationPrefilters[ index ].call( animation, elem, props, animation.opts );
+		if ( result ) {
+			return result;
+		}
+	}
+
+	jQuery.map( props, createTween, animation );
+
+	if ( jQuery.isFunction( animation.opts.start ) ) {
+		animation.opts.start.call( elem, animation );
+	}
+
+	jQuery.fx.timer(
+		jQuery.extend( tick, {
+			elem: elem,
+			anim: animation,
+			queue: animation.opts.queue
+		})
+	);
+
+	// attach callbacks from options
+	return animation.progress( animation.opts.progress )
+		.done( animation.opts.done, animation.opts.complete )
+		.fail( animation.opts.fail )
+		.always( animation.opts.always );
+}
+
+jQuery.Animation = jQuery.extend( Animation, {
+
+	tweener: function( props, callback ) {
+		if ( jQuery.isFunction( props ) ) {
+			callback = props;
+			props = [ "*" ];
+		} else {
+			props = props.split(" ");
+		}
+
+		var prop,
+			index = 0,
+			length = props.length;
+
+		for ( ; index < length ; index++ ) {
+			prop = props[ index ];
+			tweeners[ prop ] = tweeners[ prop ] || [];
+			tweeners[ prop ].unshift( callback );
+		}
+	},
+
+	prefilter: function( callback, prepend ) {
+		if ( prepend ) {
+			animationPrefilters.unshift( callback );
+		} else {
+			animationPrefilters.push( callback );
+		}
+	}
+});
+
+jQuery.speed = function( speed, easing, fn ) {
+	var opt = speed && typeof speed === "object" ? jQuery.extend( {}, speed ) : {
+		complete: fn || !fn && easing ||
+			jQuery.isFunction( speed ) && speed,
+		duration: speed,
+		easing: fn && easing || easing && !jQuery.isFunction( easing ) && easing
+	};
+
+	opt.duration = jQuery.fx.off ? 0 : typeof opt.duration === "number" ? opt.duration :
+		opt.duration in jQuery.fx.speeds ? jQuery.fx.speeds[ opt.duration ] : jQuery.fx.speeds._default;
+
+	// normalize opt.queue - true/undefined/null -> "fx"
+	if ( opt.queue == null || opt.queue === true ) {
+		opt.queue = "fx";
+	}
+
+	// Queueing
+	opt.old = opt.complete;
+
+	opt.complete = function() {
+		if ( jQuery.isFunction( opt.old ) ) {
+			opt.old.call( this );
+		}
+
+		if ( opt.queue ) {
+			jQuery.dequeue( this, opt.queue );
+		}
+	};
+
+	return opt;
+};
+
+jQuery.fn.extend({
+	fadeTo: function( speed, to, easing, callback ) {
+
+		// show any hidden elements after setting opacity to 0
+		return this.filter( isHidden ).css( "opacity", 0 ).show()
+
+			// animate to the value specified
+			.end().animate({ opacity: to }, speed, easing, callback );
+	},
+	animate: function( prop, speed, easing, callback ) {
+		var empty = jQuery.isEmptyObject( prop ),
+			optall = jQuery.speed( speed, easing, callback ),
+			doAnimation = function() {
+				// Operate on a copy of prop so per-property easing won't be lost
+				var anim = Animation( this, jQuery.extend( {}, prop ), optall );
+
+				// Empty animations, or finishing resolves immediately
+				if ( empty || data_priv.get( this, "finish" ) ) {
+					anim.stop( true );
+				}
+			};
+			doAnimation.finish = doAnimation;
+
+		return empty || optall.queue === false ?
+			this.each( doAnimation ) :
+			this.queue( optall.queue, doAnimation );
+	},
+	stop: function( type, clearQueue, gotoEnd ) {
+		var stopQueue = function( hooks ) {
+			var stop = hooks.stop;
+			delete hooks.stop;
+			stop( gotoEnd );
+		};
+
+		if ( typeof type !== "string" ) {
+			gotoEnd = clearQueue;
+			clearQueue = type;
+			type = undefined;
+		}
+		if ( clearQueue && type !== false ) {
+			this.queue( type || "fx", [] );
+		}
+
+		return this.each(function() {
+			var dequeue = true,
+				index = type != null && type + "queueHooks",
+				timers = jQuery.timers,
+				data = data_priv.get( this );
+
+			if ( index ) {
+				if ( data[ index ] && data[ index ].stop ) {
+					stopQueue( data[ index ] );
+				}
+			} else {
+				for ( index in data ) {
+					if ( data[ index ] && data[ index ].stop && rrun.test( index ) ) {
+						stopQueue( data[ index ] );
+					}
+				}
+			}
+
+			for ( index = timers.length; index--; ) {
+				if ( timers[ index ].elem === this && (type == null || timers[ index ].queue === type) ) {
+					timers[ index ].anim.stop( gotoEnd );
+					dequeue = false;
+					timers.splice( index, 1 );
+				}
+			}
+
+			// start the next in the queue if the last step wasn't forced
+			// timers currently will call their complete callbacks, which will dequeue
+			// but only if they were gotoEnd
+			if ( dequeue || !gotoEnd ) {
+				jQuery.dequeue( this, type );
+			}
+		});
+	},
+	finish: function( type ) {
+		if ( type !== false ) {
+			type = type || "fx";
+		}
+		return this.each(function() {
+			var index,
+				data = data_priv.get( this ),
+				queue = data[ type + "queue" ],
+				hooks = data[ type + "queueHooks" ],
+				timers = jQuery.timers,
+				length = queue ? queue.length : 0;
+
+			// enable finishing flag on private data
+			data.finish = true;
+
+			// empty the queue first
+			jQuery.queue( this, type, [] );
+
+			if ( hooks && hooks.stop ) {
+				hooks.stop.call( this, true );
+			}
+
+			// look for any active animations, and finish them
+			for ( index = timers.length; index--; ) {
+				if ( timers[ index ].elem === this && timers[ index ].queue === type ) {
+					timers[ index ].anim.stop( true );
+					timers.splice( index, 1 );
+				}
+			}
+
+			// look for any animations in the old queue and finish them
+			for ( index = 0; index < length; index++ ) {
+				if ( queue[ index ] && queue[ index ].finish ) {
+					queue[ index ].finish.call( this );
+				}
+			}
+
+			// turn off finishing flag
+			delete data.finish;
+		});
+	}
+});
+
+jQuery.each([ "toggle", "show", "hide" ], function( i, name ) {
+	var cssFn = jQuery.fn[ name ];
+	jQuery.fn[ name ] = function( speed, easing, callback ) {
+		return speed == null || typeof speed === "boolean" ?
+			cssFn.apply( this, arguments ) :
+			this.animate( genFx( name, true ), speed, easing, callback );
+	};
+});
+
+// Generate shortcuts for custom animations
+jQuery.each({
+	slideDown: genFx("show"),
+	slideUp: genFx("hide"),
+	slideToggle: genFx("toggle"),
+	fadeIn: { opacity: "show" },
+	fadeOut: { opacity: "hide" },
+	fadeToggle: { opacity: "toggle" }
+}, function( name, props ) {
+	jQuery.fn[ name ] = function( speed, easing, callback ) {
+		return this.animate( props, speed, easing, callback );
+	};
+});
+
+jQuery.timers = [];
+jQuery.fx.tick = function() {
+	var timer,
+		i = 0,
+		timers = jQuery.timers;
+
+	fxNow = jQuery.now();
+
+	for ( ; i < timers.length; i++ ) {
+		timer = timers[ i ];
+		// Checks the timer has not already been removed
+		if ( !timer() && timers[ i ] === timer ) {
+			timers.splice( i--, 1 );
+		}
+	}
+
+	if ( !timers.length ) {
+		jQuery.fx.stop();
+	}
+	fxNow = undefined;
+};
+
+jQuery.fx.timer = function( timer ) {
+	jQuery.timers.push( timer );
+	if ( timer() ) {
+		jQuery.fx.start();
+	} else {
+		jQuery.timers.pop();
+	}
+};
+
+jQuery.fx.interval = 13;
+
+jQuery.fx.start = function() {
+	if ( !timerId ) {
+		timerId = setInterval( jQuery.fx.tick, jQuery.fx.interval );
+	}
+};
+
+jQuery.fx.stop = function() {
+	clearInterval( timerId );
+	timerId = null;
+};
+
+jQuery.fx.speeds = {
+	slow: 600,
+	fast: 200,
+	// Default speed
+	_default: 400
+};
+
+
+// Based off of the plugin by Clint Helfers, with permission.
+// http://blindsignals.com/index.php/2009/07/jquery-delay/
+jQuery.fn.delay = function( time, type ) {
+	time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time;
+	type = type || "fx";
+
+	return this.queue( type, function( next, hooks ) {
+		var timeout = setTimeout( next, time );
+		hooks.stop = function() {
+			clearTimeout( timeout );
+		};
+	});
+};
+
+
+(function() {
+	var input = document.createElement( "input" ),
+		select = document.createElement( "select" ),
+		opt = select.appendChild( document.createElement( "option" ) );
+
+	input.type = "checkbox";
+
+	// Support: iOS 5.1, Android 4.x, Android 2.3
+	// Check the default checkbox/radio value ("" on old WebKit; "on" elsewhere)
+	support.checkOn = input.value !== "";
+
+	// Must access the parent to make an option select properly
+	// Support: IE9, IE10
+	support.optSelected = opt.selected;
+
+	// Make sure that the options inside disabled selects aren't marked as disabled
+	// (WebKit marks them as disabled)
+	select.disabled = true;
+	support.optDisabled = !opt.disabled;
+
+	// Check if an input maintains its value after becoming a radio
+	// Support: IE9, IE10
+	input = document.createElement( "input" );
+	input.value = "t";
+	input.type = "radio";
+	support.radioValue = input.value === "t";
+})();
+
+
+var nodeHook, boolHook,
+	attrHandle = jQuery.expr.attrHandle;
+
+jQuery.fn.extend({
+	attr: function( name, value ) {
+		return access( this, jQuery.attr, name, value, arguments.length > 1 );
+	},
+
+	removeAttr: function( name ) {
+		return this.each(function() {
+			jQuery.removeAttr( this, name );
+		});
+	}
+});
+
+jQuery.extend({
+	attr: function( elem, name, value ) {
+		var hooks, ret,
+			nType = elem.nodeType;
+
+		// don't get/set attributes on text, comment and attribute nodes
+		if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
+			return;
+		}
+
+		// Fallback to prop when attributes are not supported
+		if ( typeof elem.getAttribute === strundefined ) {
+			return jQuery.prop( elem, name, value );
+		}
+
+		// All attributes are lowercase
+		// Grab necessary hook if one is defined
+		if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) {
+			name = name.toLowerCase();
+			hooks = jQuery.attrHooks[ name ] ||
+				( jQuery.expr.match.bool.test( name ) ? boolHook : nodeHook );
+		}
+
+		if ( value !== undefined ) {
+
+			if ( value === null ) {
+				jQuery.removeAttr( elem, name );
+
+			} else if ( hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) {
+				return ret;
+
+			} else {
+				elem.setAttribute( name, value + "" );
+				return value;
+			}
+
+		} else if ( hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) {
+			return ret;
+
+		} else {
+			ret = jQuery.find.attr( elem, name );
+
+			// Non-existent attributes return null, we normalize to undefined
+			return ret == null ?
+				undefined :
+				ret;
+		}
+	},
+
+	removeAttr: function( elem, value ) {
+		var name, propName,
+			i = 0,
+			attrNames = value && value.match( rnotwhite );
+
+		if ( attrNames && elem.nodeType === 1 ) {
+			while ( (name = attrNames[i++]) ) {
+				propName = jQuery.propFix[ name ] || name;
+
+				// Boolean attributes get special treatment (#10870)
+				if ( jQuery.expr.match.bool.test( name ) ) {
+					// Set corresponding property to false
+					elem[ propName ] = false;
+				}
+
+				elem.removeAttribute( name );
+			}
+		}
+	},
+
+	attrHooks: {
+		type: {
+			set: function( elem, value ) {
+				if ( !support.radioValue && value === "radio" &&
+					jQuery.nodeName( elem, "input" ) ) {
+					// Setting the type on a radio button after the value resets the value in IE6-9
+					// Reset value to default in case type is set after value during creation
+					var val = elem.value;
+					elem.setAttribute( "type", value );
+					if ( val ) {
+						elem.value = val;
+					}
+					return value;
+				}
+			}
+		}
+	}
+});
+
+// Hooks for boolean attributes
+boolHook = {
+	set: function( elem, value, name ) {
+		if ( value === false ) {
+			// Remove boolean attributes when set to false
+			jQuery.removeAttr( elem, name );
+		} else {
+			elem.setAttribute( name, name );
+		}
+		return name;
+	}
+};
+jQuery.each( jQuery.expr.match.bool.source.match( /\w+/g ), function( i, name ) {
+	var getter = attrHandle[ name ] || jQuery.find.attr;
+
+	attrHandle[ name ] = function( elem, name, isXML ) {
+		var ret, handle;
+		if ( !isXML ) {
+			// Avoid an infinite loop by temporarily removing this function from the getter
+			handle = attrHandle[ name ];
+			attrHandle[ name ] = ret;
+			ret = getter( elem, name, isXML ) != null ?
+				name.toLowerCase() :
+				null;
+			attrHandle[ name ] = handle;
+		}
+		return ret;
+	};
+});
+
+
+
+
+var rfocusable = /^(?:input|select|textarea|button)$/i;
+
+jQuery.fn.extend({
+	prop: function( name, value ) {
+		return access( this, jQuery.prop, name, value, arguments.length > 1 );
+	},
+
+	removeProp: function( name ) {
+		return this.each(function() {
+			delete this[ jQuery.propFix[ name ] || name ];
+		});
+	}
+});
+
+jQuery.extend({
+	propFix: {
+		"for": "htmlFor",
+		"class": "className"
+	},
+
+	prop: function( elem, name, value ) {
+		var ret, hooks, notxml,
+			nType = elem.nodeType;
+
+		// don't get/set properties on text, comment and attribute nodes
+		if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
+			return;
+		}
+
+		notxml = nType !== 1 || !jQuery.isXMLDoc( elem );
+
+		if ( notxml ) {
+			// Fix name and attach hooks
+			name = jQuery.propFix[ name ] || name;
+			hooks = jQuery.propHooks[ name ];
+		}
+
+		if ( value !== undefined ) {
+			return hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ?
+				ret :
+				( elem[ name ] = value );
+
+		} else {
+			return hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ?
+				ret :
+				elem[ name ];
+		}
+	},
+
+	propHooks: {
+		tabIndex: {
+			get: function( elem ) {
+				return elem.hasAttribute( "tabindex" ) || rfocusable.test( elem.nodeName ) || elem.href ?
+					elem.tabIndex :
+					-1;
+			}
+		}
+	}
+});
+
+// Support: IE9+
+// Selectedness for an option in an optgroup can be inaccurate
+if ( !support.optSelected ) {
+	jQuery.propHooks.selected = {
+		get: function( elem ) {
+			var parent = elem.parentNode;
+			if ( parent && parent.parentNode ) {
+				parent.parentNode.selectedIndex;
+			}
+			return null;
+		}
+	};
+}
+
+jQuery.each([
+	"tabIndex",
+	"readOnly",
+	"maxLength",
+	"cellSpacing",
+	"cellPadding",
+	"rowSpan",
+	"colSpan",
+	"useMap",
+	"frameBorder",
+	"contentEditable"
+], function() {
+	jQuery.propFix[ this.toLowerCase() ] = this;
+});
+
+
+
+
+var rclass = /[\t\r\n\f]/g;
+
+jQuery.fn.extend({
+	addClass: function( value ) {
+		var classes, elem, cur, clazz, j, finalValue,
+			proceed = typeof value === "string" && value,
+			i = 0,
+			len = this.length;
+
+		if ( jQuery.isFunction( value ) ) {
+			return this.each(function( j ) {
+				jQuery( this ).addClass( value.call( this, j, this.className ) );
+			});
+		}
+
+		if ( proceed ) {
+			// The disjunction here is for better compressibility (see removeClass)
+			classes = ( value || "" ).match( rnotwhite ) || [];
+
+			for ( ; i < len; i++ ) {
+				elem = this[ i ];
+				cur = elem.nodeType === 1 && ( elem.className ?
+					( " " + elem.className + " " ).replace( rclass, " " ) :
+					" "
+				);
+
+				if ( cur ) {
+					j = 0;
+					while ( (clazz = classes[j++]) ) {
+						if ( cur.indexOf( " " + clazz + " " ) < 0 ) {
+							cur += clazz + " ";
+						}
+					}
+
+					// only assign if different to avoid unneeded rendering.
+					finalValue = jQuery.trim( cur );
+					if ( elem.className !== finalValue ) {
+						elem.className = finalValue;
+					}
+				}
+			}
+		}
+
+		return this;
+	},
+
+	removeClass: function( value ) {
+		var classes, elem, cur, clazz, j, finalValue,
+			proceed = arguments.length === 0 || typeof value === "string" && value,
+			i = 0,
+			len = this.length;
+
+		if ( jQuery.isFunction( value ) ) {
+			return this.each(function( j ) {
+				jQuery( this ).removeClass( value.call( this, j, this.className ) );
+			});
+		}
+		if ( proceed ) {
+			classes = ( value || "" ).match( rnotwhite ) || [];
+
+			for ( ; i < len; i++ ) {
+				elem = this[ i ];
+				// This expression is here for better compressibility (see addClass)
+				cur = elem.nodeType === 1 && ( elem.className ?
+					( " " + elem.className + " " ).replace( rclass, " " ) :
+					""
+				);
+
+				if ( cur ) {
+					j = 0;
+					while ( (clazz = classes[j++]) ) {
+						// Remove *all* instances
+						while ( cur.indexOf( " " + clazz + " " ) >= 0 ) {
+							cur = cur.replace( " " + clazz + " ", " " );
+						}
+					}
+
+					// only assign if different to avoid unneeded rendering.
+					finalValue = value ? jQuery.trim( cur ) : "";
+					if ( elem.className !== finalValue ) {
+						elem.className = finalValue;
+					}
+				}
+			}
+		}
+
+		return this;
+	},
+
+	toggleClass: function( value, stateVal ) {
+		var type = typeof value;
+
+		if ( typeof stateVal === "boolean" && type === "string" ) {
+			return stateVal ? this.addClass( value ) : this.removeClass( value );
+		}
+
+		if ( jQuery.isFunction( value ) ) {
+			return this.each(function( i ) {
+				jQuery( this ).toggleClass( value.call(this, i, this.className, stateVal), stateVal );
+			});
+		}
+
+		return this.each(function() {
+			if ( type === "string" ) {
+				// toggle individual class names
+				var className,
+					i = 0,
+					self = jQuery( this ),
+					classNames = value.match( rnotwhite ) || [];
+
+				while ( (className = classNames[ i++ ]) ) {
+					// check each className given, space separated list
+					if ( self.hasClass( className ) ) {
+						self.removeClass( className );
+					} else {
+						self.addClass( className );
+					}
+				}
+
+			// Toggle whole class name
+			} else if ( type === strundefined || type === "boolean" ) {
+				if ( this.className ) {
+					// store className if set
+					data_priv.set( this, "__className__", this.className );
+				}
+
+				// If the element has a class name or if we're passed "false",
+				// then remove the whole classname (if there was one, the above saved it).
+				// Otherwise bring back whatever was previously saved (if anything),
+				// falling back to the empty string if nothing was stored.
+				this.className = this.className || value === false ? "" : data_priv.get( this, "__className__" ) || "";
+			}
+		});
+	},
+
+	hasClass: function( selector ) {
+		var className = " " + selector + " ",
+			i = 0,
+			l = this.length;
+		for ( ; i < l; i++ ) {
+			if ( this[i].nodeType === 1 && (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) >= 0 ) {
+				return true;
+			}
+		}
+
+		return false;
+	}
+});
+
+
+
+
+var rreturn = /\r/g;
+
+jQuery.fn.extend({
+	val: function( value ) {
+		var hooks, ret, isFunction,
+			elem = this[0];
+
+		if ( !arguments.length ) {
+			if ( elem ) {
+				hooks = jQuery.valHooks[ elem.type ] || jQuery.valHooks[ elem.nodeName.toLowerCase() ];
+
+				if ( hooks && "get" in hooks && (ret = hooks.get( elem, "value" )) !== undefined ) {
+					return ret;
+				}
+
+				ret = elem.value;
+
+				return typeof ret === "string" ?
+					// handle most common string cases
+					ret.replace(rreturn, "") :
+					// handle cases where value is null/undef or number
+					ret == null ? "" : ret;
+			}
+
+			return;
+		}
+
+		isFunction = jQuery.isFunction( value );
+
+		return this.each(function( i ) {
+			var val;
+
+			if ( this.nodeType !== 1 ) {
+				return;
+			}
+
+			if ( isFunction ) {
+				val = value.call( this, i, jQuery( this ).val() );
+			} else {
+				val = value;
+			}
+
+			// Treat null/undefined as ""; convert numbers to string
+			if ( val == null ) {
+				val = "";
+
+			} else if ( typeof val === "number" ) {
+				val += "";
+
+			} else if ( jQuery.isArray( val ) ) {
+				val = jQuery.map( val, function( value ) {
+					return value == null ? "" : value + "";
+				});
+			}
+
+			hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ];
+
+			// If set returns undefined, fall back to normal setting
+			if ( !hooks || !("set" in hooks) || hooks.set( this, val, "value" ) === undefined ) {
+				this.value = val;
+			}
+		});
+	}
+});
+
+jQuery.extend({
+	valHooks: {
+		option: {
+			get: function( elem ) {
+				var val = jQuery.find.attr( elem, "value" );
+				return val != null ?
+					val :
+					// Support: IE10-11+
+					// option.text throws exceptions (#14686, #14858)
+					jQuery.trim( jQuery.text( elem ) );
+			}
+		},
+		select: {
+			get: function( elem ) {
+				var value, option,
+					options = elem.options,
+					index = elem.selectedIndex,
+					one = elem.type === "select-one" || index < 0,
+					values = one ? null : [],
+					max = one ? index + 1 : options.length,
+					i = index < 0 ?
+						max :
+						one ? index : 0;
+
+				// Loop through all the selected options
+				for ( ; i < max; i++ ) {
+					option = options[ i ];
+
+					// IE6-9 doesn't update selected after form reset (#2551)
+					if ( ( option.selected || i === index ) &&
+							// Don't return options that are disabled or in a disabled optgroup
+							( support.optDisabled ? !option.disabled : option.getAttribute( "disabled" ) === null ) &&
+							( !option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" ) ) ) {
+
+						// Get the specific value for the option
+						value = jQuery( option ).val();
+
+						// We don't need an array for one selects
+						if ( one ) {
+							return value;
+						}
+
+						// Multi-Selects return an array
+						values.push( value );
+					}
+				}
+
+				return values;
+			},
+
+			set: function( elem, value ) {
+				var optionSet, option,
+					options = elem.options,
+					values = jQuery.makeArray( value ),
+					i = options.length;
+
+				while ( i-- ) {
+					option = options[ i ];
+					if ( (option.selected = jQuery.inArray( option.value, values ) >= 0) ) {
+						optionSet = true;
+					}
+				}
+
+				// force browsers to behave consistently when non-matching value is set
+				if ( !optionSet ) {
+					elem.selectedIndex = -1;
+				}
+				return values;
+			}
+		}
+	}
+});
+
+// Radios and checkboxes getter/setter
+jQuery.each([ "radio", "checkbox" ], function() {
+	jQuery.valHooks[ this ] = {
+		set: function( elem, value ) {
+			if ( jQuery.isArray( value ) ) {
+				return ( elem.checked = jQuery.inArray( jQuery(elem).val(), value ) >= 0 );
+			}
+		}
+	};
+	if ( !support.checkOn ) {
+		jQuery.valHooks[ this ].get = function( elem ) {
+			// Support: Webkit
+			// "" is returned instead of "on" if a value isn't specified
+			return elem.getAttribute("value") === null ? "on" : elem.value;
+		};
+	}
+});
+
+
+
+
+// Return jQuery for attributes-only inclusion
+
+
+jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " +
+	"mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +
+	"change select submit keydown keypress keyup error contextmenu").split(" "), function( i, name ) {
+
+	// Handle event binding
+	jQuery.fn[ name ] = function( data, fn ) {
+		return arguments.length > 0 ?
+			this.on( name, null, data, fn ) :
+			this.trigger( name );
+	};
+});
+
+jQuery.fn.extend({
+	hover: function( fnOver, fnOut ) {
+		return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );
+	},
+
+	bind: function( types, data, fn ) {
+		return this.on( types, null, data, fn );
+	},
+	unbind: function( types, fn ) {
+		return this.off( types, null, fn );
+	},
+
+	delegate: function( selector, types, data, fn ) {
+		return this.on( types, selector, data, fn );
+	},
+	undelegate: function( selector, types, fn ) {
+		// ( namespace ) or ( selector, types [, fn] )
+		return arguments.length === 1 ? this.off( selector, "**" ) : this.off( types, selector || "**", fn );
+	}
+});
+
+
+var nonce = jQuery.now();
+
+var rquery = (/\?/);
+
+
+
+// Support: Android 2.3
+// Workaround failure to string-cast null input
+jQuery.parseJSON = function( data ) {
+	return JSON.parse( data + "" );
+};
+
+
+// Cross-browser xml parsing
+jQuery.parseXML = function( data ) {
+	var xml, tmp;
+	if ( !data || typeof data !== "string" ) {
+		return null;
+	}
+
+	// Support: IE9
+	try {
+		tmp = new DOMParser();
+		xml = tmp.parseFromString( data, "text/xml" );
+	} catch ( e ) {
+		xml = undefined;
+	}
+
+	if ( !xml || xml.getElementsByTagName( "parsererror" ).length ) {
+		jQuery.error( "Invalid XML: " + data );
+	}
+	return xml;
+};
+
+
+var
+	// Document location
+	ajaxLocParts,
+	ajaxLocation,
+
+	rhash = /#.*$/,
+	rts = /([?&])_=[^&]*/,
+	rheaders = /^(.*?):[ \t]*([^\r\n]*)$/mg,
+	// #7653, #8125, #8152: local protocol detection
+	rlocalProtocol = /^(?:about|app|app-storage|.+-extension|file|res|widget):$/,
+	rnoContent = /^(?:GET|HEAD)$/,
+	rprotocol = /^\/\//,
+	rurl = /^([\w.+-]+:)(?:\/\/(?:[^\/?#]*@|)([^\/?#:]*)(?::(\d+)|)|)/,
+
+	/* Prefilters
+	 * 1) They are useful to introduce custom dataTypes (see ajax/jsonp.js for an example)
+	 * 2) These are called:
+	 *    - BEFORE asking for a transport
+	 *    - AFTER param serialization (s.data is a string if s.processData is true)
+	 * 3) key is the dataType
+	 * 4) the catchall symbol "*" can be used
+	 * 5) execution will start with transport dataType and THEN continue down to "*" if needed
+	 */
+	prefilters = {},
+
+	/* Transports bindings
+	 * 1) key is the dataType
+	 * 2) the catchall symbol "*" can be used
+	 * 3) selection will start with transport dataType and THEN go to "*" if needed
+	 */
+	transports = {},
+
+	// Avoid comment-prolog char sequence (#10098); must appease lint and evade compression
+	allTypes = "*/".concat("*");
+
+// #8138, IE may throw an exception when accessing
+// a field from window.location if document.domain has been set
+try {
+	ajaxLocation = location.href;
+} catch( e ) {
+	// Use the href attribute of an A element
+	// since IE will modify it given document.location
+	ajaxLocation = document.createElement( "a" );
+	ajaxLocation.href = "";
+	ajaxLocation = ajaxLocation.href;
+}
+
+// Segment location into parts
+ajaxLocParts = rurl.exec( ajaxLocation.toLowerCase() ) || [];
+
+// Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport
+function addToPrefiltersOrTransports( structure ) {
+
+	// dataTypeExpression is optional and defaults to "*"
+	return function( dataTypeExpression, func ) {
+
+		if ( typeof dataTypeExpression !== "string" ) {
+			func = dataTypeExpression;
+			dataTypeExpression = "*";
+		}
+
+		var dataType,
+			i = 0,
+			dataTypes = dataTypeExpression.toLowerCase().match( rnotwhite ) || [];
+
+		if ( jQuery.isFunction( func ) ) {
+			// For each dataType in the dataTypeExpression
+			while ( (dataType = dataTypes[i++]) ) {
+				// Prepend if requested
+				if ( dataType[0] === "+" ) {
+					dataType = dataType.slice( 1 ) || "*";
+					(structure[ dataType ] = structure[ dataType ] || []).unshift( func );
+
+				// Otherwise append
+				} else {
+					(structure[ dataType ] = structure[ dataType ] || []).push( func );
+				}
+			}
+		}
+	};
+}
+
+// Base inspection function for prefilters and transports
+function inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR ) {
+
+	var inspected = {},
+		seekingTransport = ( structure === transports );
+
+	function inspect( dataType ) {
+		var selected;
+		inspected[ dataType ] = true;
+		jQuery.each( structure[ dataType ] || [], function( _, prefilterOrFactory ) {
+			var dataTypeOrTransport = prefilterOrFactory( options, originalOptions, jqXHR );
+			if ( typeof dataTypeOrTransport === "string" && !seekingTransport && !inspected[ dataTypeOrTransport ] ) {
+				options.dataTypes.unshift( dataTypeOrTransport );
+				inspect( dataTypeOrTransport );
+				return false;
+			} else if ( seekingTransport ) {
+				return !( selected = dataTypeOrTransport );
+			}
+		});
+		return selected;
+	}
+
+	return inspect( options.dataTypes[ 0 ] ) || !inspected[ "*" ] && inspect( "*" );
+}
+
+// A special extend for ajax options
+// that takes "flat" options (not to be deep extended)
+// Fixes #9887
+function ajaxExtend( target, src ) {
+	var key, deep,
+		flatOptions = jQuery.ajaxSettings.flatOptions || {};
+
+	for ( key in src ) {
+		if ( src[ key ] !== undefined ) {
+			( flatOptions[ key ] ? target : ( deep || (deep = {}) ) )[ key ] = src[ key ];
+		}
+	}
+	if ( deep ) {
+		jQuery.extend( true, target, deep );
+	}
+
+	return target;
+}
+
+/* Handles responses to an ajax request:
+ * - finds the right dataType (mediates between content-type and expected dataType)
+ * - returns the corresponding response
+ */
+function ajaxHandleResponses( s, jqXHR, responses ) {
+
+	var ct, type, finalDataType, firstDataType,
+		contents = s.contents,
+		dataTypes = s.dataTypes;
+
+	// Remove auto dataType and get content-type in the process
+	while ( dataTypes[ 0 ] === "*" ) {
+		dataTypes.shift();
+		if ( ct === undefined ) {
+			ct = s.mimeType || jqXHR.getResponseHeader("Content-Type");
+		}
+	}
+
+	// Check if we're dealing with a known content-type
+	if ( ct ) {
+		for ( type in contents ) {
+			if ( contents[ type ] && contents[ type ].test( ct ) ) {
+				dataTypes.unshift( type );
+				break;
+			}
+		}
+	}
+
+	// Check to see if we have a response for the expected dataType
+	if ( dataTypes[ 0 ] in responses ) {
+		finalDataType = dataTypes[ 0 ];
+	} else {
+		// Try convertible dataTypes
+		for ( type in responses ) {
+			if ( !dataTypes[ 0 ] || s.converters[ type + " " + dataTypes[0] ] ) {
+				finalDataType = type;
+				break;
+			}
+			if ( !firstDataType ) {
+				firstDataType = type;
+			}
+		}
+		// Or just use first one
+		finalDataType = finalDataType || firstDataType;
+	}
+
+	// If we found a dataType
+	// We add the dataType to the list if needed
+	// and return the corresponding response
+	if ( finalDataType ) {
+		if ( finalDataType !== dataTypes[ 0 ] ) {
+			dataTypes.unshift( finalDataType );
+		}
+		return responses[ finalDataType ];
+	}
+}
+
+/* Chain conversions given the request and the original response
+ * Also sets the responseXXX fields on the jqXHR instance
+ */
+function ajaxConvert( s, response, jqXHR, isSuccess ) {
+	var conv2, current, conv, tmp, prev,
+		converters = {},
+		// Work with a copy of dataTypes in case we need to modify it for conversion
+		dataTypes = s.dataTypes.slice();
+
+	// Create converters map with lowercased keys
+	if ( dataTypes[ 1 ] ) {
+		for ( conv in s.converters ) {
+			converters[ conv.toLowerCase() ] = s.converters[ conv ];
+		}
+	}
+
+	current = dataTypes.shift();
+
+	// Convert to each sequential dataType
+	while ( current ) {
+
+		if ( s.responseFields[ current ] ) {
+			jqXHR[ s.responseFields[ current ] ] = response;
+		}
+
+		// Apply the dataFilter if provided
+		if ( !prev && isSuccess && s.dataFilter ) {
+			response = s.dataFilter( response, s.dataType );
+		}
+
+		prev = current;
+		current = dataTypes.shift();
+
+		if ( current ) {
+
+		// There's only work to do if current dataType is non-auto
+			if ( current === "*" ) {
+
+				current = prev;
+
+			// Convert response if prev dataType is non-auto and differs from current
+			} else if ( prev !== "*" && prev !== current ) {
+
+				// Seek a direct converter
+				conv = converters[ prev + " " + current ] || converters[ "* " + current ];
+
+				// If none found, seek a pair
+				if ( !conv ) {
+					for ( conv2 in converters ) {
+
+						// If conv2 outputs current
+						tmp = conv2.split( " " );
+						if ( tmp[ 1 ] === current ) {
+
+							// If prev can be converted to accepted input
+							conv = converters[ prev + " " + tmp[ 0 ] ] ||
+								converters[ "* " + tmp[ 0 ] ];
+							if ( conv ) {
+								// Condense equivalence converters
+								if ( conv === true ) {
+									conv = converters[ conv2 ];
+
+								// Otherwise, insert the intermediate dataType
+								} else if ( converters[ conv2 ] !== true ) {
+									current = tmp[ 0 ];
+									dataTypes.unshift( tmp[ 1 ] );
+								}
+								break;
+							}
+						}
+					}
+				}
+
+				// Apply converter (if not an equivalence)
+				if ( conv !== true ) {
+
+					// Unless errors are allowed to bubble, catch and return them
+					if ( conv && s[ "throws" ] ) {
+						response = conv( response );
+					} else {
+						try {
+							response = conv( response );
+						} catch ( e ) {
+							return { state: "parsererror", error: conv ? e : "No conversion from " + prev + " to " + current };
+						}
+					}
+				}
+			}
+		}
+	}
+
+	return { state: "success", data: response };
+}
+
+jQuery.extend({
+
+	// Counter for holding the number of active queries
+	active: 0,
+
+	// Last-Modified header cache for next request
+	lastModified: {},
+	etag: {},
+
+	ajaxSettings: {
+		url: ajaxLocation,
+		type: "GET",
+		isLocal: rlocalProtocol.test( ajaxLocParts[ 1 ] ),
+		global: true,
+		processData: true,
+		async: true,
+		contentType: "application/x-www-form-urlencoded; charset=UTF-8",
+		/*
+		timeout: 0,
+		data: null,
+		dataType: null,
+		username: null,
+		password: null,
+		cache: null,
+		throws: false,
+		traditional: false,
+		headers: {},
+		*/
+
+		accepts: {
+			"*": allTypes,
+			text: "text/plain",
+			html: "text/html",
+			xml: "application/xml, text/xml",
+			json: "application/json, text/javascript"
+		},
+
+		contents: {
+			xml: /xml/,
+			html: /html/,
+			json: /json/
+		},
+
+		responseFields: {
+			xml: "responseXML",
+			text: "responseText",
+			json: "responseJSON"
+		},
+
+		// Data converters
+		// Keys separate source (or catchall "*") and destination types with a single space
+		converters: {
+
+			// Convert anything to text
+			"* text": String,
+
+			// Text to html (true = no transformation)
+			"text html": true,
+
+			// Evaluate text as a json expression
+			"text json": jQuery.parseJSON,
+
+			// Parse text as xml
+			"text xml": jQuery.parseXML
+		},
+
+		// For options that shouldn't be deep extended:
+		// you can add your own custom options here if
+		// and when you create one that shouldn't be
+		// deep extended (see ajaxExtend)
+		flatOptions: {
+			url: true,
+			context: true
+		}
+	},
+
+	// Creates a full fledged settings object into target
+	// with both ajaxSettings and settings fields.
+	// If target is omitted, writes into ajaxSettings.
+	ajaxSetup: function( target, settings ) {
+		return settings ?
+
+			// Building a settings object
+			ajaxExtend( ajaxExtend( target, jQuery.ajaxSettings ), settings ) :
+
+			// Extending ajaxSettings
+			ajaxExtend( jQuery.ajaxSettings, target );
+	},
+
+	ajaxPrefilter: addToPrefiltersOrTransports( prefilters ),
+	ajaxTransport: addToPrefiltersOrTransports( transports ),
+
+	// Main method
+	ajax: function( url, options ) {
+
+		// If url is an object, simulate pre-1.5 signature
+		if ( typeof url === "object" ) {
+			options = url;
+			url = undefined;
+		}
+
+		// Force options to be an object
+		options = options || {};
+
+		var transport,
+			// URL without anti-cache param
+			cacheURL,
+			// Response headers
+			responseHeadersString,
+			responseHeaders,
+			// timeout handle
+			timeoutTimer,
+			// Cross-domain detection vars
+			parts,
+			// To know if global events are to be dispatched
+			fireGlobals,
+			// Loop variable
+			i,
+			// Create the final options object
+			s = jQuery.ajaxSetup( {}, options ),
+			// Callbacks context
+			callbackContext = s.context || s,
+			// Context for global events is callbackContext if it is a DOM node or jQuery collection
+			globalEventContext = s.context && ( callbackContext.nodeType || callbackContext.jquery ) ?
+				jQuery( callbackContext ) :
+				jQuery.event,
+			// Deferreds
+			deferred = jQuery.Deferred(),
+			completeDeferred = jQuery.Callbacks("once memory"),
+			// Status-dependent callbacks
+			statusCode = s.statusCode || {},
+			// Headers (they are sent all at once)
+			requestHeaders = {},
+			requestHeadersNames = {},
+			// The jqXHR state
+			state = 0,
+			// Default abort message
+			strAbort = "canceled",
+			// Fake xhr
+			jqXHR = {
+				readyState: 0,
+
+				// Builds headers hashtable if needed
+				getResponseHeader: function( key ) {
+					var match;
+					if ( state === 2 ) {
+						if ( !responseHeaders ) {
+							responseHeaders = {};
+							while ( (match = rheaders.exec( responseHeadersString )) ) {
+								responseHeaders[ match[1].toLowerCase() ] = match[ 2 ];
+							}
+						}
+						match = responseHeaders[ key.toLowerCase() ];
+					}
+					return match == null ? null : match;
+				},
+
+				// Raw string
+				getAllResponseHeaders: function() {
+					return state === 2 ? responseHeadersString : null;
+				},
+
+				// Caches the header
+				setRequestHeader: function( name, value ) {
+					var lname = name.toLowerCase();
+					if ( !state ) {
+						name = requestHeadersNames[ lname ] = requestHeadersNames[ lname ] || name;
+						requestHeaders[ name ] = value;
+					}
+					return this;
+				},
+
+				// Overrides response content-type header
+				overrideMimeType: function( type ) {
+					if ( !state ) {
+						s.mimeType = type;
+					}
+					return this;
+				},
+
+				// Status-dependent callbacks
+				statusCode: function( map ) {
+					var code;
+					if ( map ) {
+						if ( state < 2 ) {
+							for ( code in map ) {
+								// Lazy-add the new callback in a way that preserves old ones
+								statusCode[ code ] = [ statusCode[ code ], map[ code ] ];
+							}
+						} else {
+							// Execute the appropriate callbacks
+							jqXHR.always( map[ jqXHR.status ] );
+						}
+					}
+					return this;
+				},
+
+				// Cancel the request
+				abort: function( statusText ) {
+					var finalText = statusText || strAbort;
+					if ( transport ) {
+						transport.abort( finalText );
+					}
+					done( 0, finalText );
+					return this;
+				}
+			};
+
+		// Attach deferreds
+		deferred.promise( jqXHR ).complete = completeDeferred.add;
+		jqXHR.success = jqXHR.done;
+		jqXHR.error = jqXHR.fail;
+
+		// Remove hash character (#7531: and string promotion)
+		// Add protocol if not provided (prefilters might expect it)
+		// Handle falsy url in the settings object (#10093: consistency with old signature)
+		// We also use the url parameter if available
+		s.url = ( ( url || s.url || ajaxLocation ) + "" ).replace( rhash, "" )
+			.replace( rprotocol, ajaxLocParts[ 1 ] + "//" );
+
+		// Alias method option to type as per ticket #12004
+		s.type = options.method || options.type || s.method || s.type;
+
+		// Extract dataTypes list
+		s.dataTypes = jQuery.trim( s.dataType || "*" ).toLowerCase().match( rnotwhite ) || [ "" ];
+
+		// A cross-domain request is in order when we have a protocol:host:port mismatch
+		if ( s.crossDomain == null ) {
+			parts = rurl.exec( s.url.toLowerCase() );
+			s.crossDomain = !!( parts &&
+				( parts[ 1 ] !== ajaxLocParts[ 1 ] || parts[ 2 ] !== ajaxLocParts[ 2 ] ||
+					( parts[ 3 ] || ( parts[ 1 ] === "http:" ? "80" : "443" ) ) !==
+						( ajaxLocParts[ 3 ] || ( ajaxLocParts[ 1 ] === "http:" ? "80" : "443" ) ) )
+			);
+		}
+
+		// Convert data if not already a string
+		if ( s.data && s.processData && typeof s.data !== "string" ) {
+			s.data = jQuery.param( s.data, s.traditional );
+		}
+
+		// Apply prefilters
+		inspectPrefiltersOrTransports( prefilters, s, options, jqXHR );
+
+		// If request was aborted inside a prefilter, stop there
+		if ( state === 2 ) {
+			return jqXHR;
+		}
+
+		// We can fire global events as of now if asked to
+		fireGlobals = s.global;
+
+		// Watch for a new set of requests
+		if ( fireGlobals && jQuery.active++ === 0 ) {
+			jQuery.event.trigger("ajaxStart");
+		}
+
+		// Uppercase the type
+		s.type = s.type.toUpperCase();
+
+		// Determine if request has content
+		s.hasContent = !rnoContent.test( s.type );
+
+		// Save the URL in case we're toying with the If-Modified-Since
+		// and/or If-None-Match header later on
+		cacheURL = s.url;
+
+		// More options handling for requests with no content
+		if ( !s.hasContent ) {
+
+			// If data is available, append data to url
+			if ( s.data ) {
+				cacheURL = ( s.url += ( rquery.test( cacheURL ) ? "&" : "?" ) + s.data );
+				// #9682: remove data so that it's not used in an eventual retry
+				delete s.data;
+			}
+
+			// Add anti-cache in url if needed
+			if ( s.cache === false ) {
+				s.url = rts.test( cacheURL ) ?
+
+					// If there is already a '_' parameter, set its value
+					cacheURL.replace( rts, "$1_=" + nonce++ ) :
+
+					// Otherwise add one to the end
+					cacheURL + ( rquery.test( cacheURL ) ? "&" : "?" ) + "_=" + nonce++;
+			}
+		}
+
+		// Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
+		if ( s.ifModified ) {
+			if ( jQuery.lastModified[ cacheURL ] ) {
+				jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified[ cacheURL ] );
+			}
+			if ( jQuery.etag[ cacheURL ] ) {
+				jqXHR.setRequestHeader( "If-None-Match", jQuery.etag[ cacheURL ] );
+			}
+		}
+
+		// Set the correct header, if data is being sent
+		if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) {
+			jqXHR.setRequestHeader( "Content-Type", s.contentType );
+		}
+
+		// Set the Accepts header for the server, depending on the dataType
+		jqXHR.setRequestHeader(
+			"Accept",
+			s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[0] ] ?
+				s.accepts[ s.dataTypes[0] ] + ( s.dataTypes[ 0 ] !== "*" ? ", " + allTypes + "; q=0.01" : "" ) :
+				s.accepts[ "*" ]
+		);
+
+		// Check for headers option
+		for ( i in s.headers ) {
+			jqXHR.setRequestHeader( i, s.headers[ i ] );
+		}
+
+		// Allow custom headers/mimetypes and early abort
+		if ( s.beforeSend && ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || state === 2 ) ) {
+			// Abort if not done already and return
+			return jqXHR.abort();
+		}
+
+		// aborting is no longer a cancellation
+		strAbort = "abort";
+
+		// Install callbacks on deferreds
+		for ( i in { success: 1, error: 1, complete: 1 } ) {
+			jqXHR[ i ]( s[ i ] );
+		}
+
+		// Get transport
+		transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR );
+
+		// If no transport, we auto-abort
+		if ( !transport ) {
+			done( -1, "No Transport" );
+		} else {
+			jqXHR.readyState = 1;
+
+			// Send global event
+			if ( fireGlobals ) {
+				globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] );
+			}
+			// Timeout
+			if ( s.async && s.timeout > 0 ) {
+				timeoutTimer = setTimeout(function() {
+					jqXHR.abort("timeout");
+				}, s.timeout );
+			}
+
+			try {
+				state = 1;
+				transport.send( requestHeaders, done );
+			} catch ( e ) {
+				// Propagate exception as error if not done
+				if ( state < 2 ) {
+					done( -1, e );
+				// Simply rethrow otherwise
+				} else {
+					throw e;
+				}
+			}
+		}
+
+		// Callback for when everything is done
+		function done( status, nativeStatusText, responses, headers ) {
+			var isSuccess, success, error, response, modified,
+				statusText = nativeStatusText;
+
+			// Called once
+			if ( state === 2 ) {
+				return;
+			}
+
+			// State is "done" now
+			state = 2;
+
+			// Clear timeout if it exists
+			if ( timeoutTimer ) {
+				clearTimeout( timeoutTimer );
+			}
+
+			// Dereference transport for early garbage collection
+			// (no matter how long the jqXHR object will be used)
+			transport = undefined;
+
+			// Cache response headers
+			responseHeadersString = headers || "";
+
+			// Set readyState
+			jqXHR.readyState = status > 0 ? 4 : 0;
+
+			// Determine if successful
+			isSuccess = status >= 200 && status < 300 || status === 304;
+
+			// Get response data
+			if ( responses ) {
+				response = ajaxHandleResponses( s, jqXHR, responses );
+			}
+
+			// Convert no matter what (that way responseXXX fields are always set)
+			response = ajaxConvert( s, response, jqXHR, isSuccess );
+
+			// If successful, handle type chaining
+			if ( isSuccess ) {
+
+				// Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
+				if ( s.ifModified ) {
+					modified = jqXHR.getResponseHeader("Last-Modified");
+					if ( modified ) {
+						jQuery.lastModified[ cacheURL ] = modified;
+					}
+					modified = jqXHR.getResponseHeader("etag");
+					if ( modified ) {
+						jQuery.etag[ cacheURL ] = modified;
+					}
+				}
+
+				// if no content
+				if ( status === 204 || s.type === "HEAD" ) {
+					statusText = "nocontent";
+
+				// if not modified
+				} else if ( status === 304 ) {
+					statusText = "notmodified";
+
+				// If we have data, let's convert it
+				} else {
+					statusText = response.state;
+					success = response.data;
+					error = response.error;
+					isSuccess = !error;
+				}
+			} else {
+				// We extract error from statusText
+				// then normalize statusText and status for non-aborts
+				error = statusText;
+				if ( status || !statusText ) {
+					statusText = "error";
+					if ( status < 0 ) {
+						status = 0;
+					}
+				}
+			}
+
+			// Set data for the fake xhr object
+			jqXHR.status = status;
+			jqXHR.statusText = ( nativeStatusText || statusText ) + "";
+
+			// Success/Error
+			if ( isSuccess ) {
+				deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] );
+			} else {
+				deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] );
+			}
+
+			// Status-dependent callbacks
+			jqXHR.statusCode( statusCode );
+			statusCode = undefined;
+
+			if ( fireGlobals ) {
+				globalEventContext.trigger( isSuccess ? "ajaxSuccess" : "ajaxError",
+					[ jqXHR, s, isSuccess ? success : error ] );
+			}
+
+			// Complete
+			completeDeferred.fireWith( callbackContext, [ jqXHR, statusText ] );
+
+			if ( fireGlobals ) {
+				globalEventContext.trigger( "ajaxComplete", [ jqXHR, s ] );
+				// Handle the global AJAX counter
+				if ( !( --jQuery.active ) ) {
+					jQuery.event.trigger("ajaxStop");
+				}
+			}
+		}
+
+		return jqXHR;
+	},
+
+	getJSON: function( url, data, callback ) {
+		return jQuery.get( url, data, callback, "json" );
+	},
+
+	getScript: function( url, callback ) {
+		return jQuery.get( url, undefined, callback, "script" );
+	}
+});
+
+jQuery.each( [ "get", "post" ], function( i, method ) {
+	jQuery[ method ] = function( url, data, callback, type ) {
+		// shift arguments if data argument was omitted
+		if ( jQuery.isFunction( data ) ) {
+			type = type || callback;
+			callback = data;
+			data = undefined;
+		}
+
+		return jQuery.ajax({
+			url: url,
+			type: method,
+			dataType: type,
+			data: data,
+			success: callback
+		});
+	};
+});
+
+// Attach a bunch of functions for handling common AJAX events
+jQuery.each( [ "ajaxStart", "ajaxStop", "ajaxComplete", "ajaxError", "ajaxSuccess", "ajaxSend" ], function( i, type ) {
+	jQuery.fn[ type ] = function( fn ) {
+		return this.on( type, fn );
+	};
+});
+
+
+jQuery._evalUrl = function( url ) {
+	return jQuery.ajax({
+		url: url,
+		type: "GET",
+		dataType: "script",
+		async: false,
+		global: false,
+		"throws": true
+	});
+};
+
+
+jQuery.fn.extend({
+	wrapAll: function( html ) {
+		var wrap;
+
+		if ( jQuery.isFunction( html ) ) {
+			return this.each(function( i ) {
+				jQuery( this ).wrapAll( html.call(this, i) );
+			});
+		}
+
+		if ( this[ 0 ] ) {
+
+			// The elements to wrap the target around
+			wrap = jQuery( html, this[ 0 ].ownerDocument ).eq( 0 ).clone( true );
+
+			if ( this[ 0 ].parentNode ) {
+				wrap.insertBefore( this[ 0 ] );
+			}
+
+			wrap.map(function() {
+				var elem = this;
+
+				while ( elem.firstElementChild ) {
+					elem = elem.firstElementChild;
+				}
+
+				return elem;
+			}).append( this );
+		}
+
+		return this;
+	},
+
+	wrapInner: function( html ) {
+		if ( jQuery.isFunction( html ) ) {
+			return this.each(function( i ) {
+				jQuery( this ).wrapInner( html.call(this, i) );
+			});
+		}
+
+		return this.each(function() {
+			var self = jQuery( this ),
+				contents = self.contents();
+
+			if ( contents.length ) {
+				contents.wrapAll( html );
+
+			} else {
+				self.append( html );
+			}
+		});
+	},
+
+	wrap: function( html ) {
+		var isFunction = jQuery.isFunction( html );
+
+		return this.each(function( i ) {
+			jQuery( this ).wrapAll( isFunction ? html.call(this, i) : html );
+		});
+	},
+
+	unwrap: function() {
+		return this.parent().each(function() {
+			if ( !jQuery.nodeName( this, "body" ) ) {
+				jQuery( this ).replaceWith( this.childNodes );
+			}
+		}).end();
+	}
+});
+
+
+jQuery.expr.filters.hidden = function( elem ) {
+	// Support: Opera <= 12.12
+	// Opera reports offsetWidths and offsetHeights less than zero on some elements
+	return elem.offsetWidth <= 0 && elem.offsetHeight <= 0;
+};
+jQuery.expr.filters.visible = function( elem ) {
+	return !jQuery.expr.filters.hidden( elem );
+};
+
+
+
+
+var r20 = /%20/g,
+	rbracket = /\[\]$/,
+	rCRLF = /\r?\n/g,
+	rsubmitterTypes = /^(?:submit|button|image|reset|file)$/i,
+	rsubmittable = /^(?:input|select|textarea|keygen)/i;
+
+function buildParams( prefix, obj, traditional, add ) {
+	var name;
+
+	if ( jQuery.isArray( obj ) ) {
+		// Serialize array item.
+		jQuery.each( obj, function( i, v ) {
+			if ( traditional || rbracket.test( prefix ) ) {
+				// Treat each array item as a scalar.
+				add( prefix, v );
+
+			} else {
+				// Item is non-scalar (array or object), encode its numeric index.
+				buildParams( prefix + "[" + ( typeof v === "object" ? i : "" ) + "]", v, traditional, add );
+			}
+		});
+
+	} else if ( !traditional && jQuery.type( obj ) === "object" ) {
+		// Serialize object item.
+		for ( name in obj ) {
+			buildParams( prefix + "[" + name + "]", obj[ name ], traditional, add );
+		}
+
+	} else {
+		// Serialize scalar item.
+		add( prefix, obj );
+	}
+}
+
+// Serialize an array of form elements or a set of
+// key/values into a query string
+jQuery.param = function( a, traditional ) {
+	var prefix,
+		s = [],
+		add = function( key, value ) {
+			// If value is a function, invoke it and return its value
+			value = jQuery.isFunction( value ) ? value() : ( value == null ? "" : value );
+			s[ s.length ] = encodeURIComponent( key ) + "=" + encodeURIComponent( value );
+		};
+
+	// Set traditional to true for jQuery <= 1.3.2 behavior.
+	if ( traditional === undefined ) {
+		traditional = jQuery.ajaxSettings && jQuery.ajaxSettings.traditional;
+	}
+
+	// If an array was passed in, assume that it is an array of form elements.
+	if ( jQuery.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) {
+		// Serialize the form elements
+		jQuery.each( a, function() {
+			add( this.name, this.value );
+		});
+
+	} else {
+		// If traditional, encode the "old" way (the way 1.3.2 or older
+		// did it), otherwise encode params recursively.
+		for ( prefix in a ) {
+			buildParams( prefix, a[ prefix ], traditional, add );
+		}
+	}
+
+	// Return the resulting serialization
+	return s.join( "&" ).replace( r20, "+" );
+};
+
+jQuery.fn.extend({
+	serialize: function() {
+		return jQuery.param( this.serializeArray() );
+	},
+	serializeArray: function() {
+		return this.map(function() {
+			// Can add propHook for "elements" to filter or add form elements
+			var elements = jQuery.prop( this, "elements" );
+			return elements ? jQuery.makeArray( elements ) : this;
+		})
+		.filter(function() {
+			var type = this.type;
+
+			// Use .is( ":disabled" ) so that fieldset[disabled] works
+			return this.name && !jQuery( this ).is( ":disabled" ) &&
+				rsubmittable.test( this.nodeName ) && !rsubmitterTypes.test( type ) &&
+				( this.checked || !rcheckableType.test( type ) );
+		})
+		.map(function( i, elem ) {
+			var val = jQuery( this ).val();
+
+			return val == null ?
+				null :
+				jQuery.isArray( val ) ?
+					jQuery.map( val, function( val ) {
+						return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
+					}) :
+					{ name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
+		}).get();
+	}
+});
+
+
+jQuery.ajaxSettings.xhr = function() {
+	try {
+		return new XMLHttpRequest();
+	} catch( e ) {}
+};
+
+var xhrId = 0,
+	xhrCallbacks = {},
+	xhrSuccessStatus = {
+		// file protocol always yields status code 0, assume 200
+		0: 200,
+		// Support: IE9
+		// #1450: sometimes IE returns 1223 when it should be 204
+		1223: 204
+	},
+	xhrSupported = jQuery.ajaxSettings.xhr();
+
+// Support: IE9
+// Open requests must be manually aborted on unload (#5280)
+if ( window.ActiveXObject ) {
+	jQuery( window ).on( "unload", function() {
+		for ( var key in xhrCallbacks ) {
+			xhrCallbacks[ key ]();
+		}
+	});
+}
+
+support.cors = !!xhrSupported && ( "withCredentials" in xhrSupported );
+support.ajax = xhrSupported = !!xhrSupported;
+
+jQuery.ajaxTransport(function( options ) {
+	var callback;
+
+	// Cross domain only allowed if supported through XMLHttpRequest
+	if ( support.cors || xhrSupported && !options.crossDomain ) {
+		return {
+			send: function( headers, complete ) {
+				var i,
+					xhr = options.xhr(),
+					id = ++xhrId;
+
+				xhr.open( options.type, options.url, options.async, options.username, options.password );
+
+				// Apply custom fields if provided
+				if ( options.xhrFields ) {
+					for ( i in options.xhrFields ) {
+						xhr[ i ] = options.xhrFields[ i ];
+					}
+				}
+
+				// Override mime type if needed
+				if ( options.mimeType && xhr.overrideMimeType ) {
+					xhr.overrideMimeType( options.mimeType );
+				}
+
+				// X-Requested-With header
+				// For cross-domain requests, seeing as conditions for a preflight are
+				// akin to a jigsaw puzzle, we simply never set it to be sure.
+				// (it can always be set on a per-request basis or even using ajaxSetup)
+				// For same-domain requests, won't change header if already provided.
+				if ( !options.crossDomain && !headers["X-Requested-With"] ) {
+					headers["X-Requested-With"] = "XMLHttpRequest";
+				}
+
+				// Set headers
+				for ( i in headers ) {
+					xhr.setRequestHeader( i, headers[ i ] );
+				}
+
+				// Callback
+				callback = function( type ) {
+					return function() {
+						if ( callback ) {
+							delete xhrCallbacks[ id ];
+							callback = xhr.onload = xhr.onerror = null;
+
+							if ( type === "abort" ) {
+								xhr.abort();
+							} else if ( type === "error" ) {
+								complete(
+									// file: protocol always yields status 0; see #8605, #14207
+									xhr.status,
+									xhr.statusText
+								);
+							} else {
+								complete(
+									xhrSuccessStatus[ xhr.status ] || xhr.status,
+									xhr.statusText,
+									// Support: IE9
+									// Accessing binary-data responseText throws an exception
+									// (#11426)
+									typeof xhr.responseText === "string" ? {
+										text: xhr.responseText
+									} : undefined,
+									xhr.getAllResponseHeaders()
+								);
+							}
+						}
+					};
+				};
+
+				// Listen to events
+				xhr.onload = callback();
+				xhr.onerror = callback("error");
+
+				// Create the abort callback
+				callback = xhrCallbacks[ id ] = callback("abort");
+
+				try {
+					// Do send the request (this may raise an exception)
+					xhr.send( options.hasContent && options.data || null );
+				} catch ( e ) {
+					// #14683: Only rethrow if this hasn't been notified as an error yet
+					if ( callback ) {
+						throw e;
+					}
+				}
+			},
+
+			abort: function() {
+				if ( callback ) {
+					callback();
+				}
+			}
+		};
+	}
+});
+
+
+
+
+// Install script dataType
+jQuery.ajaxSetup({
+	accepts: {
+		script: "text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"
+	},
+	contents: {
+		script: /(?:java|ecma)script/
+	},
+	converters: {
+		"text script": function( text ) {
+			jQuery.globalEval( text );
+			return text;
+		}
+	}
+});
+
+// Handle cache's special case and crossDomain
+jQuery.ajaxPrefilter( "script", function( s ) {
+	if ( s.cache === undefined ) {
+		s.cache = false;
+	}
+	if ( s.crossDomain ) {
+		s.type = "GET";
+	}
+});
+
+// Bind script tag hack transport
+jQuery.ajaxTransport( "script", function( s ) {
+	// This transport only deals with cross domain requests
+	if ( s.crossDomain ) {
+		var script, callback;
+		return {
+			send: function( _, complete ) {
+				script = jQuery("<script>").prop({
+					async: true,
+					charset: s.scriptCharset,
+					src: s.url
+				}).on(
+					"load error",
+					callback = function( evt ) {
+						script.remove();
+						callback = null;
+						if ( evt ) {
+							complete( evt.type === "error" ? 404 : 200, evt.type );
+						}
+					}
+				);
+				document.head.appendChild( script[ 0 ] );
+			},
+			abort: function() {
+				if ( callback ) {
+					callback();
+				}
+			}
+		};
+	}
+});
+
+
+
+
+var oldCallbacks = [],
+	rjsonp = /(=)\?(?=&|$)|\?\?/;
+
+// Default jsonp settings
+jQuery.ajaxSetup({
+	jsonp: "callback",
+	jsonpCallback: function() {
+		var callback = oldCallbacks.pop() || ( jQuery.expando + "_" + ( nonce++ ) );
+		this[ callback ] = true;
+		return callback;
+	}
+});
+
+// Detect, normalize options and install callbacks for jsonp requests
+jQuery.ajaxPrefilter( "json jsonp", function( s, originalSettings, jqXHR ) {
+
+	var callbackName, overwritten, responseContainer,
+		jsonProp = s.jsonp !== false && ( rjsonp.test( s.url ) ?
+			"url" :
+			typeof s.data === "string" && !( s.contentType || "" ).indexOf("application/x-www-form-urlencoded") && rjsonp.test( s.data ) && "data"
+		);
+
+	// Handle iff the expected data type is "jsonp" or we have a parameter to set
+	if ( jsonProp || s.dataTypes[ 0 ] === "jsonp" ) {
+
+		// Get callback name, remembering preexisting value associated with it
+		callbackName = s.jsonpCallback = jQuery.isFunction( s.jsonpCallback ) ?
+			s.jsonpCallback() :
+			s.jsonpCallback;
+
+		// Insert callback into url or form data
+		if ( jsonProp ) {
+			s[ jsonProp ] = s[ jsonProp ].replace( rjsonp, "$1" + callbackName );
+		} else if ( s.jsonp !== false ) {
+			s.url += ( rquery.test( s.url ) ? "&" : "?" ) + s.jsonp + "=" + callbackName;
+		}
+
+		// Use data converter to retrieve json after script execution
+		s.converters["script json"] = function() {
+			if ( !responseContainer ) {
+				jQuery.error( callbackName + " was not called" );
+			}
+			return responseContainer[ 0 ];
+		};
+
+		// force json dataType
+		s.dataTypes[ 0 ] = "json";
+
+		// Install callback
+		overwritten = window[ callbackName ];
+		window[ callbackName ] = function() {
+			responseContainer = arguments;
+		};
+
+		// Clean-up function (fires after converters)
+		jqXHR.always(function() {
+			// Restore preexisting value
+			window[ callbackName ] = overwritten;
+
+			// Save back as free
+			if ( s[ callbackName ] ) {
+				// make sure that re-using the options doesn't screw things around
+				s.jsonpCallback = originalSettings.jsonpCallback;
+
+				// save the callback name for future use
+				oldCallbacks.push( callbackName );
+			}
+
+			// Call if it was a function and we have a response
+			if ( responseContainer && jQuery.isFunction( overwritten ) ) {
+				overwritten( responseContainer[ 0 ] );
+			}
+
+			responseContainer = overwritten = undefined;
+		});
+
+		// Delegate to script
+		return "script";
+	}
+});
+
+
+
+
+// data: string of html
+// context (optional): If specified, the fragment will be created in this context, defaults to document
+// keepScripts (optional): If true, will include scripts passed in the html string
+jQuery.parseHTML = function( data, context, keepScripts ) {
+	if ( !data || typeof data !== "string" ) {
+		return null;
+	}
+	if ( typeof context === "boolean" ) {
+		keepScripts = context;
+		context = false;
+	}
+	context = context || document;
+
+	var parsed = rsingleTag.exec( data ),
+		scripts = !keepScripts && [];
+
+	// Single tag
+	if ( parsed ) {
+		return [ context.createElement( parsed[1] ) ];
+	}
+
+	parsed = jQuery.buildFragment( [ data ], context, scripts );
+
+	if ( scripts && scripts.length ) {
+		jQuery( scripts ).remove();
+	}
+
+	return jQuery.merge( [], parsed.childNodes );
+};
+
+
+// Keep a copy of the old load method
+var _load = jQuery.fn.load;
+
+/**
+ * Load a url into a page
+ */
+jQuery.fn.load = function( url, params, callback ) {
+	if ( typeof url !== "string" && _load ) {
+		return _load.apply( this, arguments );
+	}
+
+	var selector, type, response,
+		self = this,
+		off = url.indexOf(" ");
+
+	if ( off >= 0 ) {
+		selector = jQuery.trim( url.slice( off ) );
+		url = url.slice( 0, off );
+	}
+
+	// If it's a function
+	if ( jQuery.isFunction( params ) ) {
+
+		// We assume that it's the callback
+		callback = params;
+		params = undefined;
+
+	// Otherwise, build a param string
+	} else if ( params && typeof params === "object" ) {
+		type = "POST";
+	}
+
+	// If we have elements to modify, make the request
+	if ( self.length > 0 ) {
+		jQuery.ajax({
+			url: url,
+
+			// if "type" variable is undefined, then "GET" method will be used
+			type: type,
+			dataType: "html",
+			data: params
+		}).done(function( responseText ) {
+
+			// Save response for use in complete callback
+			response = arguments;
+
+			self.html( selector ?
+
+				// If a selector was specified, locate the right elements in a dummy div
+				// Exclude scripts to avoid IE 'Permission Denied' errors
+				jQuery("<div>").append( jQuery.parseHTML( responseText ) ).find( selector ) :
+
+				// Otherwise use the full result
+				responseText );
+
+		}).complete( callback && function( jqXHR, status ) {
+			self.each( callback, response || [ jqXHR.responseText, status, jqXHR ] );
+		});
+	}
+
+	return this;
+};
+
+
+
+
+jQuery.expr.filters.animated = function( elem ) {
+	return jQuery.grep(jQuery.timers, function( fn ) {
+		return elem === fn.elem;
+	}).length;
+};
+
+
+
+
+var docElem = window.document.documentElement;
+
+/**
+ * Gets a window from an element
+ */
+function getWindow( elem ) {
+	return jQuery.isWindow( elem ) ? elem : elem.nodeType === 9 && elem.defaultView;
+}
+
+jQuery.offset = {
+	setOffset: function( elem, options, i ) {
+		var curPosition, curLeft, curCSSTop, curTop, curOffset, curCSSLeft, calculatePosition,
+			position = jQuery.css( elem, "position" ),
+			curElem = jQuery( elem ),
+			props = {};
+
+		// Set position first, in-case top/left are set even on static elem
+		if ( position === "static" ) {
+			elem.style.position = "relative";
+		}
+
+		curOffset = curElem.offset();
+		curCSSTop = jQuery.css( elem, "top" );
+		curCSSLeft = jQuery.css( elem, "left" );
+		calculatePosition = ( position === "absolute" || position === "fixed" ) &&
+			( curCSSTop + curCSSLeft ).indexOf("auto") > -1;
+
+		// Need to be able to calculate position if either top or left is auto and position is either absolute or fixed
+		if ( calculatePosition ) {
+			curPosition = curElem.position();
+			curTop = curPosition.top;
+			curLeft = curPosition.left;
+
+		} else {
+			curTop = parseFloat( curCSSTop ) || 0;
+			curLeft = parseFloat( curCSSLeft ) || 0;
+		}
+
+		if ( jQuery.isFunction( options ) ) {
+			options = options.call( elem, i, curOffset );
+		}
+
+		if ( options.top != null ) {
+			props.top = ( options.top - curOffset.top ) + curTop;
+		}
+		if ( options.left != null ) {
+			props.left = ( options.left - curOffset.left ) + curLeft;
+		}
+
+		if ( "using" in options ) {
+			options.using.call( elem, props );
+
+		} else {
+			curElem.css( props );
+		}
+	}
+};
+
+jQuery.fn.extend({
+	offset: function( options ) {
+		if ( arguments.length ) {
+			return options === undefined ?
+				this :
+				this.each(function( i ) {
+					jQuery.offset.setOffset( this, options, i );
+				});
+		}
+
+		var docElem, win,
+			elem = this[ 0 ],
+			box = { top: 0, left: 0 },
+			doc = elem && elem.ownerDocument;
+
+		if ( !doc ) {
+			return;
+		}
+
+		docElem = doc.documentElement;
+
+		// Make sure it's not a disconnected DOM node
+		if ( !jQuery.contains( docElem, elem ) ) {
+			return box;
+		}
+
+		// If we don't have gBCR, just use 0,0 rather than error
+		// BlackBerry 5, iOS 3 (original iPhone)
+		if ( typeof elem.getBoundingClientRect !== strundefined ) {
+			box = elem.getBoundingClientRect();
+		}
+		win = getWindow( doc );
+		return {
+			top: box.top + win.pageYOffset - docElem.clientTop,
+			left: box.left + win.pageXOffset - docElem.clientLeft
+		};
+	},
+
+	position: function() {
+		if ( !this[ 0 ] ) {
+			return;
+		}
+
+		var offsetParent, offset,
+			elem = this[ 0 ],
+			parentOffset = { top: 0, left: 0 };
+
+		// Fixed elements are offset from window (parentOffset = {top:0, left: 0}, because it is its only offset parent
+		if ( jQuery.css( elem, "position" ) === "fixed" ) {
+			// We assume that getBoundingClientRect is available when computed position is fixed
+			offset = elem.getBoundingClientRect();
+
+		} else {
+			// Get *real* offsetParent
+			offsetParent = this.offsetParent();
+
+			// Get correct offsets
+			offset = this.offset();
+			if ( !jQuery.nodeName( offsetParent[ 0 ], "html" ) ) {
+				parentOffset = offsetParent.offset();
+			}
+
+			// Add offsetParent borders
+			parentOffset.top += jQuery.css( offsetParent[ 0 ], "borderTopWidth", true );
+			parentOffset.left += jQuery.css( offsetParent[ 0 ], "borderLeftWidth", true );
+		}
+
+		// Subtract parent offsets and element margins
+		return {
+			top: offset.top - parentOffset.top - jQuery.css( elem, "marginTop", true ),
+			left: offset.left - parentOffset.left - jQuery.css( elem, "marginLeft", true )
+		};
+	},
+
+	offsetParent: function() {
+		return this.map(function() {
+			var offsetParent = this.offsetParent || docElem;
+
+			while ( offsetParent && ( !jQuery.nodeName( offsetParent, "html" ) && jQuery.css( offsetParent, "position" ) === "static" ) ) {
+				offsetParent = offsetParent.offsetParent;
+			}
+
+			return offsetParent || docElem;
+		});
+	}
+});
+
+// Create scrollLeft and scrollTop methods
+jQuery.each( { scrollLeft: "pageXOffset", scrollTop: "pageYOffset" }, function( method, prop ) {
+	var top = "pageYOffset" === prop;
+
+	jQuery.fn[ method ] = function( val ) {
+		return access( this, function( elem, method, val ) {
+			var win = getWindow( elem );
+
+			if ( val === undefined ) {
+				return win ? win[ prop ] : elem[ method ];
+			}
+
+			if ( win ) {
+				win.scrollTo(
+					!top ? val : window.pageXOffset,
+					top ? val : window.pageYOffset
+				);
+
+			} else {
+				elem[ method ] = val;
+			}
+		}, method, val, arguments.length, null );
+	};
+});
+
+// Add the top/left cssHooks using jQuery.fn.position
+// Webkit bug: https://bugs.webkit.org/show_bug.cgi?id=29084
+// getComputedStyle returns percent when specified for top/left/bottom/right
+// rather than make the css module depend on the offset module, we just check for it here
+jQuery.each( [ "top", "left" ], function( i, prop ) {
+	jQuery.cssHooks[ prop ] = addGetHookIf( support.pixelPosition,
+		function( elem, computed ) {
+			if ( computed ) {
+				computed = curCSS( elem, prop );
+				// if curCSS returns percentage, fallback to offset
+				return rnumnonpx.test( computed ) ?
+					jQuery( elem ).position()[ prop ] + "px" :
+					computed;
+			}
+		}
+	);
+});
+
+
+// Create innerHeight, innerWidth, height, width, outerHeight and outerWidth methods
+jQuery.each( { Height: "height", Width: "width" }, function( name, type ) {
+	jQuery.each( { padding: "inner" + name, content: type, "": "outer" + name }, function( defaultExtra, funcName ) {
+		// margin is only for outerHeight, outerWidth
+		jQuery.fn[ funcName ] = function( margin, value ) {
+			var chainable = arguments.length && ( defaultExtra || typeof margin !== "boolean" ),
+				extra = defaultExtra || ( margin === true || value === true ? "margin" : "border" );
+
+			return access( this, function( elem, type, value ) {
+				var doc;
+
+				if ( jQuery.isWindow( elem ) ) {
+					// As of 5/8/2012 this will yield incorrect results for Mobile Safari, but there
+					// isn't a whole lot we can do. See pull request at this URL for discussion:
+					// https://github.com/jquery/jquery/pull/764
+					return elem.document.documentElement[ "client" + name ];
+				}
+
+				// Get document width or height
+				if ( elem.nodeType === 9 ) {
+					doc = elem.documentElement;
+
+					// Either scroll[Width/Height] or offset[Width/Height] or client[Width/Height],
+					// whichever is greatest
+					return Math.max(
+						elem.body[ "scroll" + name ], doc[ "scroll" + name ],
+						elem.body[ "offset" + name ], doc[ "offset" + name ],
+						doc[ "client" + name ]
+					);
+				}
+
+				return value === undefined ?
+					// Get width or height on the element, requesting but not forcing parseFloat
+					jQuery.css( elem, type, extra ) :
+
+					// Set width or height on the element
+					jQuery.style( elem, type, value, extra );
+			}, type, chainable ? margin : undefined, chainable, null );
+		};
+	});
+});
+
+
+// The number of elements contained in the matched element set
+jQuery.fn.size = function() {
+	return this.length;
+};
+
+jQuery.fn.andSelf = jQuery.fn.addBack;
+
+
+
+
+// Register as a named AMD module, since jQuery can be concatenated with other
+// files that may use define, but not via a proper concatenation script that
+// understands anonymous AMD modules. A named AMD is safest and most robust
+// way to register. Lowercase jquery is used because AMD module names are
+// derived from file names, and jQuery is normally delivered in a lowercase
+// file name. Do this after creating the global so that if an AMD module wants
+// to call noConflict to hide this version of jQuery, it will work.
+
+// Note that for maximum portability, libraries that are not jQuery should
+// declare themselves as anonymous modules, and avoid setting a global if an
+// AMD loader is present. jQuery is a special case. For more information, see
+// https://github.com/jrburke/requirejs/wiki/Updating-existing-libraries#wiki-anon
+
+if ( typeof define === "function" && define.amd ) {
+	define( "jquery", [], function() {
+		return jQuery;
+	});
+}
+
+
+
+
+var
+	// Map over jQuery in case of overwrite
+	_jQuery = window.jQuery,
+
+	// Map over the $ in case of overwrite
+	_$ = window.$;
+
+jQuery.noConflict = function( deep ) {
+	if ( window.$ === jQuery ) {
+		window.$ = _$;
+	}
+
+	if ( deep && window.jQuery === jQuery ) {
+		window.jQuery = _jQuery;
+	}
+
+	return jQuery;
+};
+
+// Expose jQuery and $ identifiers, even in
+// AMD (#7102#comment:10, https://github.com/jquery/jquery/pull/557)
+// and CommonJS for browser emulators (#13566)
+if ( typeof noGlobal === strundefined ) {
+	window.jQuery = window.$ = jQuery;
+}
+
+
+
+
+return jQuery;
+
+}));
diff --git a/packages/context-coloring/benchmark/fixtures/lodash-2.4.1.js b/packages/context-coloring/benchmark/fixtures/lodash-2.4.1.js
new file mode 100644
index 0000000..d653e5a
--- /dev/null
+++ b/packages/context-coloring/benchmark/fixtures/lodash-2.4.1.js
@@ -0,0 +1,6785 @@
+/**
+ * @license
+ * Lo-Dash 2.4.1 (Custom Build) <http://lodash.com/>
+ * Build: `lodash modern -o ./dist/lodash.js`
+ * Copyright 2012-2013 The Dojo Foundation <http://dojofoundation.org/>
+ * Based on Underscore.js 1.5.2 <http://underscorejs.org/LICENSE>
+ * Copyright 2009-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
+ * Available under MIT license <http://lodash.com/license>
+ */
+;(function() {
+
+  /** Used as a safe reference for `undefined` in pre ES5 environments */
+  var undefined;
+
+  /** Used to pool arrays and objects used internally */
+  var arrayPool = [],
+      objectPool = [];
+
+  /** Used to generate unique IDs */
+  var idCounter = 0;
+
+  /** Used to prefix keys to avoid issues with `__proto__` and properties on `Object.prototype` */
+  var keyPrefix = +new Date + '';
+
+  /** Used as the size when optimizations are enabled for large arrays */
+  var largeArraySize = 75;
+
+  /** Used as the max size of the `arrayPool` and `objectPool` */
+  var maxPoolSize = 40;
+
+  /** Used to detect and test whitespace */
+  var whitespace = (
+    // whitespace
+    ' \t\x0B\f\xA0\ufeff' +
+
+    // line terminators
+    '\n\r\u2028\u2029' +
+
+    // unicode category "Zs" space separators
+    '\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000'
+  );
+
+  /** Used to match empty string literals in compiled template source */
+  var reEmptyStringLeading = /\b__p \+= '';/g,
+      reEmptyStringMiddle = /\b(__p \+=) '' \+/g,
+      reEmptyStringTrailing = /(__e\(.*?\)|\b__t\)) \+\n'';/g;
+
+  /**
+   * Used to match ES6 template delimiters
+   * http://people.mozilla.org/~jorendorff/es6-draft.html#sec-literals-string-literals
+   */
+  var reEsTemplate = /\$\{([^\\}]*(?:\\.[^\\}]*)*)\}/g;
+
+  /** Used to match regexp flags from their coerced string values */
+  var reFlags = /\w*$/;
+
+  /** Used to detected named functions */
+  var reFuncName = /^\s*function[ \n\r\t]+\w/;
+
+  /** Used to match "interpolate" template delimiters */
+  var reInterpolate = /<%=([\s\S]+?)%>/g;
+
+  /** Used to match leading whitespace and zeros to be removed */
+  var reLeadingSpacesAndZeros = RegExp('^[' + whitespace + ']*0+(?=.$)');
+
+  /** Used to ensure capturing order of template delimiters */
+  var reNoMatch = /($^)/;
+
+  /** Used to detect functions containing a `this` reference */
+  var reThis = /\bthis\b/;
+
+  /** Used to match unescaped characters in compiled string literals */
+  var reUnescapedString = /['\n\r\t\u2028\u2029\\]/g;
+
+  /** Used to assign default `context` object properties */
+  var contextProps = [
+    'Array', 'Boolean', 'Date', 'Function', 'Math', 'Number', 'Object',
+    'RegExp', 'String', '_', 'attachEvent', 'clearTimeout', 'isFinite', 'isNaN',
+    'parseInt', 'setTimeout'
+  ];
+
+  /** Used to make template sourceURLs easier to identify */
+  var templateCounter = 0;
+
+  /** `Object#toString` result shortcuts */
+  var argsClass = '[object Arguments]',
+      arrayClass = '[object Array]',
+      boolClass = '[object Boolean]',
+      dateClass = '[object Date]',
+      funcClass = '[object Function]',
+      numberClass = '[object Number]',
+      objectClass = '[object Object]',
+      regexpClass = '[object RegExp]',
+      stringClass = '[object String]';
+
+  /** Used to identify object classifications that `_.clone` supports */
+  var cloneableClasses = {};
+  cloneableClasses[funcClass] = false;
+  cloneableClasses[argsClass] = cloneableClasses[arrayClass] =
+  cloneableClasses[boolClass] = cloneableClasses[dateClass] =
+  cloneableClasses[numberClass] = cloneableClasses[objectClass] =
+  cloneableClasses[regexpClass] = cloneableClasses[stringClass] = true;
+
+  /** Used as an internal `_.debounce` options object */
+  var debounceOptions = {
+    'leading': false,
+    'maxWait': 0,
+    'trailing': false
+  };
+
+  /** Used as the property descriptor for `__bindData__` */
+  var descriptor = {
+    'configurable': false,
+    'enumerable': false,
+    'value': null,
+    'writable': false
+  };
+
+  /** Used to determine if values are of the language type Object */
+  var objectTypes = {
+    'boolean': false,
+    'function': true,
+    'object': true,
+    'number': false,
+    'string': false,
+    'undefined': false
+  };
+
+  /** Used to escape characters for inclusion in compiled string literals */
+  var stringEscapes = {
+    '\\': '\\',
+    "'": "'",
+    '\n': 'n',
+    '\r': 'r',
+    '\t': 't',
+    '\u2028': 'u2028',
+    '\u2029': 'u2029'
+  };
+
+  /** Used as a reference to the global object */
+  var root = (objectTypes[typeof window] && window) || this;
+
+  /** Detect free variable `exports` */
+  var freeExports = objectTypes[typeof exports] && exports && !exports.nodeType && exports;
+
+  /** Detect free variable `module` */
+  var freeModule = objectTypes[typeof module] && module && !module.nodeType && module;
+
+  /** Detect the popular CommonJS extension `module.exports` */
+  var moduleExports = freeModule && freeModule.exports === freeExports && freeExports;
+
+  /** Detect free variable `global` from Node.js or Browserified code and use it as `root` */
+  var freeGlobal = objectTypes[typeof global] && global;
+  if (freeGlobal && (freeGlobal.global === freeGlobal || freeGlobal.window === freeGlobal)) {
+    root = freeGlobal;
+  }
+
+  /*--------------------------------------------------------------------------*/
+
+  /**
+   * The base implementation of `_.indexOf` without support for binary searches
+   * or `fromIndex` constraints.
+   *
+   * @private
+   * @param {Array} array The array to search.
+   * @param {*} value The value to search for.
+   * @param {number} [fromIndex=0] The index to search from.
+   * @returns {number} Returns the index of the matched value or `-1`.
+   */
+  function baseIndexOf(array, value, fromIndex) {
+    var index = (fromIndex || 0) - 1,
+        length = array ? array.length : 0;
+
+    while (++index < length) {
+      if (array[index] === value) {
+        return index;
+      }
+    }
+    return -1;
+  }
+
+  /**
+   * An implementation of `_.contains` for cache objects that mimics the return
+   * signature of `_.indexOf` by returning `0` if the value is found, else `-1`.
+   *
+   * @private
+   * @param {Object} cache The cache object to inspect.
+   * @param {*} value The value to search for.
+   * @returns {number} Returns `0` if `value` is found, else `-1`.
+   */
+  function cacheIndexOf(cache, value) {
+    var type = typeof value;
+    cache = cache.cache;
+
+    if (type == 'boolean' || value == null) {
+      return cache[value] ? 0 : -1;
+    }
+    if (type != 'number' && type != 'string') {
+      type = 'object';
+    }
+    var key = type == 'number' ? value : keyPrefix + value;
+    cache = (cache = cache[type]) && cache[key];
+
+    return type == 'object'
+      ? (cache && baseIndexOf(cache, value) > -1 ? 0 : -1)
+      : (cache ? 0 : -1);
+  }
+
+  /**
+   * Adds a given value to the corresponding cache object.
+   *
+   * @private
+   * @param {*} value The value to add to the cache.
+   */
+  function cachePush(value) {
+    var cache = this.cache,
+        type = typeof value;
+
+    if (type == 'boolean' || value == null) {
+      cache[value] = true;
+    } else {
+      if (type != 'number' && type != 'string') {
+        type = 'object';
+      }
+      var key = type == 'number' ? value : keyPrefix + value,
+          typeCache = cache[type] || (cache[type] = {});
+
+      if (type == 'object') {
+        (typeCache[key] || (typeCache[key] = [])).push(value);
+      } else {
+        typeCache[key] = true;
+      }
+    }
+  }
+
+  /**
+   * Used by `_.max` and `_.min` as the default callback when a given
+   * collection is a string value.
+   *
+   * @private
+   * @param {string} value The character to inspect.
+   * @returns {number} Returns the code unit of given character.
+   */
+  function charAtCallback(value) {
+    return value.charCodeAt(0);
+  }
+
+  /**
+   * Used by `sortBy` to compare transformed `collection` elements, stable sorting
+   * them in ascending order.
+   *
+   * @private
+   * @param {Object} a The object to compare to `b`.
+   * @param {Object} b The object to compare to `a`.
+   * @returns {number} Returns the sort order indicator of `1` or `-1`.
+   */
+  function compareAscending(a, b) {
+    var ac = a.criteria,
+        bc = b.criteria,
+        index = -1,
+        length = ac.length;
+
+    while (++index < length) {
+      var value = ac[index],
+          other = bc[index];
+
+      if (value !== other) {
+        if (value > other || typeof value == 'undefined') {
+          return 1;
+        }
+        if (value < other || typeof other == 'undefined') {
+          return -1;
+        }
+      }
+    }
+    // Fixes an `Array#sort` bug in the JS engine embedded in Adobe applications
+    // that causes it, under certain circumstances, to return the same value for
+    // `a` and `b`. See https://github.com/jashkenas/underscore/pull/1247
+    //
+    // This also ensures a stable sort in V8 and other engines.
+    // See http://code.google.com/p/v8/issues/detail?id=90
+    return a.index - b.index;
+  }
+
+  /**
+   * Creates a cache object to optimize linear searches of large arrays.
+   *
+   * @private
+   * @param {Array} [array=[]] The array to search.
+   * @returns {null|Object} Returns the cache object or `null` if caching should not be used.
+   */
+  function createCache(array) {
+    var index = -1,
+        length = array.length,
+        first = array[0],
+        mid = array[(length / 2) | 0],
+        last = array[length - 1];
+
+    if (first && typeof first == 'object' &&
+        mid && typeof mid == 'object' && last && typeof last == 'object') {
+      return false;
+    }
+    var cache = getObject();
+    cache['false'] = cache['null'] = cache['true'] = cache['undefined'] = false;
+
+    var result = getObject();
+    result.array = array;
+    result.cache = cache;
+    result.push = cachePush;
+
+    while (++index < length) {
+      result.push(array[index]);
+    }
+    return result;
+  }
+
+  /**
+   * Used by `template` to escape characters for inclusion in compiled
+   * string literals.
+   *
+   * @private
+   * @param {string} match The matched character to escape.
+   * @returns {string} Returns the escaped character.
+   */
+  function escapeStringChar(match) {
+    return '\\' + stringEscapes[match];
+  }
+
+  /**
+   * Gets an array from the array pool or creates a new one if the pool is empty.
+   *
+   * @private
+   * @returns {Array} The array from the pool.
+   */
+  function getArray() {
+    return arrayPool.pop() || [];
+  }
+
+  /**
+   * Gets an object from the object pool or creates a new one if the pool is empty.
+   *
+   * @private
+   * @returns {Object} The object from the pool.
+   */
+  function getObject() {
+    return objectPool.pop() || {
+      'array': null,
+      'cache': null,
+      'criteria': null,
+      'false': false,
+      'index': 0,
+      'null': false,
+      'number': null,
+      'object': null,
+      'push': null,
+      'string': null,
+      'true': false,
+      'undefined': false,
+      'value': null
+    };
+  }
+
+  /**
+   * Releases the given array back to the array pool.
+   *
+   * @private
+   * @param {Array} [array] The array to release.
+   */
+  function releaseArray(array) {
+    array.length = 0;
+    if (arrayPool.length < maxPoolSize) {
+      arrayPool.push(array);
+    }
+  }
+
+  /**
+   * Releases the given object back to the object pool.
+   *
+   * @private
+   * @param {Object} [object] The object to release.
+   */
+  function releaseObject(object) {
+    var cache = object.cache;
+    if (cache) {
+      releaseObject(cache);
+    }
+    object.array = object.cache = object.criteria = object.object = object.number = object.string = object.value = null;
+    if (objectPool.length < maxPoolSize) {
+      objectPool.push(object);
+    }
+  }
+
+  /**
+   * Slices the `collection` from the `start` index up to, but not including,
+   * the `end` index.
+   *
+   * Note: This function is used instead of `Array#slice` to support node lists
+   * in IE < 9 and to ensure dense arrays are returned.
+   *
+   * @private
+   * @param {Array|Object|string} collection The collection to slice.
+   * @param {number} start The start index.
+   * @param {number} end The end index.
+   * @returns {Array} Returns the new array.
+   */
+  function slice(array, start, end) {
+    start || (start = 0);
+    if (typeof end == 'undefined') {
+      end = array ? array.length : 0;
+    }
+    var index = -1,
+        length = end - start || 0,
+        result = Array(length < 0 ? 0 : length);
+
+    while (++index < length) {
+      result[index] = array[start + index];
+    }
+    return result;
+  }
+
+  /*--------------------------------------------------------------------------*/
+
+  /**
+   * Create a new `lodash` function using the given context object.
+   *
+   * @static
+   * @memberOf _
+   * @category Utilities
+   * @param {Object} [context=root] The context object.
+   * @returns {Function} Returns the `lodash` function.
+   */
+  function runInContext(context) {
+    // Avoid issues with some ES3 environments that attempt to use values, named
+    // after built-in constructors like `Object`, for the creation of literals.
+    // ES5 clears this up by stating that literals must use built-in constructors.
+    // See http://es5.github.io/#x11.1.5.
+    context = context ? _.defaults(root.Object(), context, _.pick(root, contextProps)) : root;
+
+    /** Native constructor references */
+    var Array = context.Array,
+        Boolean = context.Boolean,
+        Date = context.Date,
+        Function = context.Function,
+        Math = context.Math,
+        Number = context.Number,
+        Object = context.Object,
+        RegExp = context.RegExp,
+        String = context.String,
+        TypeError = context.TypeError;
+
+    /**
+     * Used for `Array` method references.
+     *
+     * Normally `Array.prototype` would suffice, however, using an array literal
+     * avoids issues in Narwhal.
+     */
+    var arrayRef = [];
+
+    /** Used for native method references */
+    var objectProto = Object.prototype;
+
+    /** Used to restore the original `_` reference in `noConflict` */
+    var oldDash = context._;
+
+    /** Used to resolve the internal [[Class]] of values */
+    var toString = objectProto.toString;
+
+    /** Used to detect if a method is native */
+    var reNative = RegExp('^' +
+      String(toString)
+        .replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
+        .replace(/toString| for [^\]]+/g, '.*?') + '$'
+    );
+
+    /** Native method shortcuts */
+    var ceil = Math.ceil,
+        clearTimeout = context.clearTimeout,
+        floor = Math.floor,
+        fnToString = Function.prototype.toString,
+        getPrototypeOf = isNative(getPrototypeOf = Object.getPrototypeOf) && getPrototypeOf,
+        hasOwnProperty = objectProto.hasOwnProperty,
+        push = arrayRef.push,
+        setTimeout = context.setTimeout,
+        splice = arrayRef.splice,
+        unshift = arrayRef.unshift;
+
+    /** Used to set meta data on functions */
+    var defineProperty = (function() {
+      // IE 8 only accepts DOM elements
+      try {
+        var o = {},
+            func = isNative(func = Object.defineProperty) && func,
+            result = func(o, o, o) && func;
+      } catch(e) { }
+      return result;
+    }());
+
+    /* Native method shortcuts for methods with the same name as other `lodash` methods */
+    var nativeCreate = isNative(nativeCreate = Object.create) && nativeCreate,
+        nativeIsArray = isNative(nativeIsArray = Array.isArray) && nativeIsArray,
+        nativeIsFinite = context.isFinite,
+        nativeIsNaN = context.isNaN,
+        nativeKeys = isNative(nativeKeys = Object.keys) && nativeKeys,
+        nativeMax = Math.max,
+        nativeMin = Math.min,
+        nativeParseInt = context.parseInt,
+        nativeRandom = Math.random;
+
+    /** Used to lookup a built-in constructor by [[Class]] */
+    var ctorByClass = {};
+    ctorByClass[arrayClass] = Array;
+    ctorByClass[boolClass] = Boolean;
+    ctorByClass[dateClass] = Date;
+    ctorByClass[funcClass] = Function;
+    ctorByClass[objectClass] = Object;
+    ctorByClass[numberClass] = Number;
+    ctorByClass[regexpClass] = RegExp;
+    ctorByClass[stringClass] = String;
+
+    /*--------------------------------------------------------------------------*/
+
+    /**
+     * Creates a `lodash` object which wraps the given value to enable intuitive
+     * method chaining.
+     *
+     * In addition to Lo-Dash methods, wrappers also have the following `Array` methods:
+     * `concat`, `join`, `pop`, `push`, `reverse`, `shift`, `slice`, `sort`, `splice`,
+     * and `unshift`
+     *
+     * Chaining is supported in custom builds as long as the `value` method is
+     * implicitly or explicitly included in the build.
+     *
+     * The chainable wrapper functions are:
+     * `after`, `assign`, `bind`, `bindAll`, `bindKey`, `chain`, `compact`,
+     * `compose`, `concat`, `countBy`, `create`, `createCallback`, `curry`,
+     * `debounce`, `defaults`, `defer`, `delay`, `difference`, `filter`, `flatten`,
+     * `forEach`, `forEachRight`, `forIn`, `forInRight`, `forOwn`, `forOwnRight`,
+     * `functions`, `groupBy`, `indexBy`, `initial`, `intersection`, `invert`,
+     * `invoke`, `keys`, `map`, `max`, `memoize`, `merge`, `min`, `object`, `omit`,
+     * `once`, `pairs`, `partial`, `partialRight`, `pick`, `pluck`, `pull`, `push`,
+     * `range`, `reject`, `remove`, `rest`, `reverse`, `shuffle`, `slice`, `sort`,
+     * `sortBy`, `splice`, `tap`, `throttle`, `times`, `toArray`, `transform`,
+     * `union`, `uniq`, `unshift`, `unzip`, `values`, `where`, `without`, `wrap`,
+     * and `zip`
+     *
+     * The non-chainable wrapper functions are:
+     * `clone`, `cloneDeep`, `contains`, `escape`, `every`, `find`, `findIndex`,
+     * `findKey`, `findLast`, `findLastIndex`, `findLastKey`, `has`, `identity`,
+     * `indexOf`, `isArguments`, `isArray`, `isBoolean`, `isDate`, `isElement`,
+     * `isEmpty`, `isEqual`, `isFinite`, `isFunction`, `isNaN`, `isNull`, `isNumber`,
+     * `isObject`, `isPlainObject`, `isRegExp`, `isString`, `isUndefined`, `join`,
+     * `lastIndexOf`, `mixin`, `noConflict`, `parseInt`, `pop`, `random`, `reduce`,
+     * `reduceRight`, `result`, `shift`, `size`, `some`, `sortedIndex`, `runInContext`,
+     * `template`, `unescape`, `uniqueId`, and `value`
+     *
+     * The wrapper functions `first` and `last` return wrapped values when `n` is
+     * provided, otherwise they return unwrapped values.
+     *
+     * Explicit chaining can be enabled by using the `_.chain` method.
+     *
+     * @name _
+     * @constructor
+     * @category Chaining
+     * @param {*} value The value to wrap in a `lodash` instance.
+     * @returns {Object} Returns a `lodash` instance.
+     * @example
+     *
+     * var wrapped = _([1, 2, 3]);
+     *
+     * // returns an unwrapped value
+     * wrapped.reduce(function(sum, num) {
+     *   return sum + num;
+     * });
+     * // => 6
+     *
+     * // returns a wrapped value
+     * var squares = wrapped.map(function(num) {
+     *   return num * num;
+     * });
+     *
+     * _.isArray(squares);
+     * // => false
+     *
+     * _.isArray(squares.value());
+     * // => true
+     */
+    function lodash(value) {
+      // don't wrap if already wrapped, even if wrapped by a different `lodash` constructor
+      return (value && typeof value == 'object' && !isArray(value) && hasOwnProperty.call(value, '__wrapped__'))
+       ? value
+       : new lodashWrapper(value);
+    }
+
+    /**
+     * A fast path for creating `lodash` wrapper objects.
+     *
+     * @private
+     * @param {*} value The value to wrap in a `lodash` instance.
+     * @param {boolean} chainAll A flag to enable chaining for all methods
+     * @returns {Object} Returns a `lodash` instance.
+     */
+    function lodashWrapper(value, chainAll) {
+      this.__chain__ = !!chainAll;
+      this.__wrapped__ = value;
+    }
+    // ensure `new lodashWrapper` is an instance of `lodash`
+    lodashWrapper.prototype = lodash.prototype;
+
+    /**
+     * An object used to flag environments features.
+     *
+     * @static
+     * @memberOf _
+     * @type Object
+     */
+    var support = lodash.support = {};
+
+    /**
+     * Detect if functions can be decompiled by `Function#toString`
+     * (all but PS3 and older Opera mobile browsers & avoided in Windows 8 apps).
+     *
+     * @memberOf _.support
+     * @type boolean
+     */
+    support.funcDecomp = !isNative(context.WinRTError) && reThis.test(runInContext);
+
+    /**
+     * Detect if `Function#name` is supported (all but IE).
+     *
+     * @memberOf _.support
+     * @type boolean
+     */
+    support.funcNames = typeof Function.name == 'string';
+
+    /**
+     * By default, the template delimiters used by Lo-Dash are similar to those in
+     * embedded Ruby (ERB). Change the following template settings to use alternative
+     * delimiters.
+     *
+     * @static
+     * @memberOf _
+     * @type Object
+     */
+    lodash.templateSettings = {
+
+      /**
+       * Used to detect `data` property values to be HTML-escaped.
+       *
+       * @memberOf _.templateSettings
+       * @type RegExp
+       */
+      'escape': /<%-([\s\S]+?)%>/g,
+
+      /**
+       * Used to detect code to be evaluated.
+       *
+       * @memberOf _.templateSettings
+       * @type RegExp
+       */
+      'evaluate': /<%([\s\S]+?)%>/g,
+
+      /**
+       * Used to detect `data` property values to inject.
+       *
+       * @memberOf _.templateSettings
+       * @type RegExp
+       */
+      'interpolate': reInterpolate,
+
+      /**
+       * Used to reference the data object in the template text.
+       *
+       * @memberOf _.templateSettings
+       * @type string
+       */
+      'variable': '',
+
+      /**
+       * Used to import variables into the compiled template.
+       *
+       * @memberOf _.templateSettings
+       * @type Object
+       */
+      'imports': {
+
+        /**
+         * A reference to the `lodash` function.
+         *
+         * @memberOf _.templateSettings.imports
+         * @type Function
+         */
+        '_': lodash
+      }
+    };
+
+    /*--------------------------------------------------------------------------*/
+
+    /**
+     * The base implementation of `_.bind` that creates the bound function and
+     * sets its meta data.
+     *
+     * @private
+     * @param {Array} bindData The bind data array.
+     * @returns {Function} Returns the new bound function.
+     */
+    function baseBind(bindData) {
+      var func = bindData[0],
+          partialArgs = bindData[2],
+          thisArg = bindData[4];
+
+      function bound() {
+        // `Function#bind` spec
+        // http://es5.github.io/#x15.3.4.5
+        if (partialArgs) {
+          // avoid `arguments` object deoptimizations by using `slice` instead
+          // of `Array.prototype.slice.call` and not assigning `arguments` to a
+          // variable as a ternary expression
+          var args = slice(partialArgs);
+          push.apply(args, arguments);
+        }
+        // mimic the constructor's `return` behavior
+        // http://es5.github.io/#x13.2.2
+        if (this instanceof bound) {
+          // ensure `new bound` is an instance of `func`
+          var thisBinding = baseCreate(func.prototype),
+              result = func.apply(thisBinding, args || arguments);
+          return isObject(result) ? result : thisBinding;
+        }
+        return func.apply(thisArg, args || arguments);
+      }
+      setBindData(bound, bindData);
+      return bound;
+    }
+
+    /**
+     * The base implementation of `_.clone` without argument juggling or support
+     * for `thisArg` binding.
+     *
+     * @private
+     * @param {*} value The value to clone.
+     * @param {boolean} [isDeep=false] Specify a deep clone.
+     * @param {Function} [callback] The function to customize cloning values.
+     * @param {Array} [stackA=[]] Tracks traversed source objects.
+     * @param {Array} [stackB=[]] Associates clones with source counterparts.
+     * @returns {*} Returns the cloned value.
+     */
+    function baseClone(value, isDeep, callback, stackA, stackB) {
+      if (callback) {
+        var result = callback(value);
+        if (typeof result != 'undefined') {
+          return result;
+        }
+      }
+      // inspect [[Class]]
+      var isObj = isObject(value);
+      if (isObj) {
+        var className = toString.call(value);
+        if (!cloneableClasses[className]) {
+          return value;
+        }
+        var ctor = ctorByClass[className];
+        switch (className) {
+          case boolClass:
+          case dateClass:
+            return new ctor(+value);
+
+          case numberClass:
+          case stringClass:
+            return new ctor(value);
+
+          case regexpClass:
+            result = ctor(value.source, reFlags.exec(value));
+            result.lastIndex = value.lastIndex;
+            return result;
+        }
+      } else {
+        return value;
+      }
+      var isArr = isArray(value);
+      if (isDeep) {
+        // check for circular references and return corresponding clone
+        var initedStack = !stackA;
+        stackA || (stackA = getArray());
+        stackB || (stackB = getArray());
+
+        var length = stackA.length;
+        while (length--) {
+          if (stackA[length] == value) {
+            return stackB[length];
+          }
+        }
+        result = isArr ? ctor(value.length) : {};
+      }
+      else {
+        result = isArr ? slice(value) : assign({}, value);
+      }
+      // add array properties assigned by `RegExp#exec`
+      if (isArr) {
+        if (hasOwnProperty.call(value, 'index')) {
+          result.index = value.index;
+        }
+        if (hasOwnProperty.call(value, 'input')) {
+          result.input = value.input;
+        }
+      }
+      // exit for shallow clone
+      if (!isDeep) {
+        return result;
+      }
+      // add the source value to the stack of traversed objects
+      // and associate it with its clone
+      stackA.push(value);
+      stackB.push(result);
+
+      // recursively populate clone (susceptible to call stack limits)
+      (isArr ? forEach : forOwn)(value, function(objValue, key) {
+        result[key] = baseClone(objValue, isDeep, callback, stackA, stackB);
+      });
+
+      if (initedStack) {
+        releaseArray(stackA);
+        releaseArray(stackB);
+      }
+      return result;
+    }
+
+    /**
+     * The base implementation of `_.create` without support for assigning
+     * properties to the created object.
+     *
+     * @private
+     * @param {Object} prototype The object to inherit from.
+     * @returns {Object} Returns the new object.
+     */
+    function baseCreate(prototype, properties) {
+      return isObject(prototype) ? nativeCreate(prototype) : {};
+    }
+    // fallback for browsers without `Object.create`
+    if (!nativeCreate) {
+      baseCreate = (function() {
+        function Object() {}
+        return function(prototype) {
+          if (isObject(prototype)) {
+            Object.prototype = prototype;
+            var result = new Object;
+            Object.prototype = null;
+          }
+          return result || context.Object();
+        };
+      }());
+    }
+
+    /**
+     * The base implementation of `_.createCallback` without support for creating
+     * "_.pluck" or "_.where" style callbacks.
+     *
+     * @private
+     * @param {*} [func=identity] The value to convert to a callback.
+     * @param {*} [thisArg] The `this` binding of the created callback.
+     * @param {number} [argCount] The number of arguments the callback accepts.
+     * @returns {Function} Returns a callback function.
+     */
+    function baseCreateCallback(func, thisArg, argCount) {
+      if (typeof func != 'function') {
+        return identity;
+      }
+      // exit early for no `thisArg` or already bound by `Function#bind`
+      if (typeof thisArg == 'undefined' || !('prototype' in func)) {
+        return func;
+      }
+      var bindData = func.__bindData__;
+      if (typeof bindData == 'undefined') {
+        if (support.funcNames) {
+          bindData = !func.name;
+        }
+        bindData = bindData || !support.funcDecomp;
+        if (!bindData) {
+          var source = fnToString.call(func);
+          if (!support.funcNames) {
+            bindData = !reFuncName.test(source);
+          }
+          if (!bindData) {
+            // checks if `func` references the `this` keyword and stores the result
+            bindData = reThis.test(source);
+            setBindData(func, bindData);
+          }
+        }
+      }
+      // exit early if there are no `this` references or `func` is bound
+      if (bindData === false || (bindData !== true && bindData[1] & 1)) {
+        return func;
+      }
+      switch (argCount) {
+        case 1: return function(value) {
+          return func.call(thisArg, value);
+        };
+        case 2: return function(a, b) {
+          return func.call(thisArg, a, b);
+        };
+        case 3: return function(value, index, collection) {
+          return func.call(thisArg, value, index, collection);
+        };
+        case 4: return function(accumulator, value, index, collection) {
+          return func.call(thisArg, accumulator, value, index, collection);
+        };
+      }
+      return bind(func, thisArg);
+    }
+
+    /**
+     * The base implementation of `createWrapper` that creates the wrapper and
+     * sets its meta data.
+     *
+     * @private
+     * @param {Array} bindData The bind data array.
+     * @returns {Function} Returns the new function.
+     */
+    function baseCreateWrapper(bindData) {
+      var func = bindData[0],
+          bitmask = bindData[1],
+          partialArgs = bindData[2],
+          partialRightArgs = bindData[3],
+          thisArg = bindData[4],
+          arity = bindData[5];
+
+      var isBind = bitmask & 1,
+          isBindKey = bitmask & 2,
+          isCurry = bitmask & 4,
+          isCurryBound = bitmask & 8,
+          key = func;
+
+      function bound() {
+        var thisBinding = isBind ? thisArg : this;
+        if (partialArgs) {
+          var args = slice(partialArgs);
+          push.apply(args, arguments);
+        }
+        if (partialRightArgs || isCurry) {
+          args || (args = slice(arguments));
+          if (partialRightArgs) {
+            push.apply(args, partialRightArgs);
+          }
+          if (isCurry && args.length < arity) {
+            bitmask |= 16 & ~32;
+            return baseCreateWrapper([func, (isCurryBound ? bitmask : bitmask & ~3), args, null, thisArg, arity]);
+          }
+        }
+        args || (args = arguments);
+        if (isBindKey) {
+          func = thisBinding[key];
+        }
+        if (this instanceof bound) {
+          thisBinding = baseCreate(func.prototype);
+          var result = func.apply(thisBinding, args);
+          return isObject(result) ? result : thisBinding;
+        }
+        return func.apply(thisBinding, args);
+      }
+      setBindData(bound, bindData);
+      return bound;
+    }
+
+    /**
+     * The base implementation of `_.difference` that accepts a single array
+     * of values to exclude.
+     *
+     * @private
+     * @param {Array} array The array to process.
+     * @param {Array} [values] The array of values to exclude.
+     * @returns {Array} Returns a new array of filtered values.
+     */
+    function baseDifference(array, values) {
+      var index = -1,
+          indexOf = getIndexOf(),
+          length = array ? array.length : 0,
+          isLarge = length >= largeArraySize && indexOf === baseIndexOf,
+          result = [];
+
+      if (isLarge) {
+        var cache = createCache(values);
+        if (cache) {
+          indexOf = cacheIndexOf;
+          values = cache;
+        } else {
+          isLarge = false;
+        }
+      }
+      while (++index < length) {
+        var value = array[index];
+        if (indexOf(values, value) < 0) {
+          result.push(value);
+        }
+      }
+      if (isLarge) {
+        releaseObject(values);
+      }
+      return result;
+    }
+
+    /**
+     * The base implementation of `_.flatten` without support for callback
+     * shorthands or `thisArg` binding.
+     *
+     * @private
+     * @param {Array} array The array to flatten.
+     * @param {boolean} [isShallow=false] A flag to restrict flattening to a single level.
+     * @param {boolean} [isStrict=false] A flag to restrict flattening to arrays and `arguments` objects.
+     * @param {number} [fromIndex=0] The index to start from.
+     * @returns {Array} Returns a new flattened array.
+     */
+    function baseFlatten(array, isShallow, isStrict, fromIndex) {
+      var index = (fromIndex || 0) - 1,
+          length = array ? array.length : 0,
+          result = [];
+
+      while (++index < length) {
+        var value = array[index];
+
+        if (value && typeof value == 'object' && typeof value.length == 'number'
+            && (isArray(value) || isArguments(value))) {
+          // recursively flatten arrays (susceptible to call stack limits)
+          if (!isShallow) {
+            value = baseFlatten(value, isShallow, isStrict);
+          }
+          var valIndex = -1,
+              valLength = value.length,
+              resIndex = result.length;
+
+          result.length += valLength;
+          while (++valIndex < valLength) {
+            result[resIndex++] = value[valIndex];
+          }
+        } else if (!isStrict) {
+          result.push(value);
+        }
+      }
+      return result;
+    }
+
+    /**
+     * The base implementation of `_.isEqual`, without support for `thisArg` binding,
+     * that allows partial "_.where" style comparisons.
+     *
+     * @private
+     * @param {*} a The value to compare.
+     * @param {*} b The other value to compare.
+     * @param {Function} [callback] The function to customize comparing values.
+     * @param {Function} [isWhere=false] A flag to indicate performing partial comparisons.
+     * @param {Array} [stackA=[]] Tracks traversed `a` objects.
+     * @param {Array} [stackB=[]] Tracks traversed `b` objects.
+     * @returns {boolean} Returns `true` if the values are equivalent, else `false`.
+     */
+    function baseIsEqual(a, b, callback, isWhere, stackA, stackB) {
+      // used to indicate that when comparing objects, `a` has at least the properties of `b`
+      if (callback) {
+        var result = callback(a, b);
+        if (typeof result != 'undefined') {
+          return !!result;
+        }
+      }
+      // exit early for identical values
+      if (a === b) {
+        // treat `+0` vs. `-0` as not equal
+        return a !== 0 || (1 / a == 1 / b);
+      }
+      var type = typeof a,
+          otherType = typeof b;
+
+      // exit early for unlike primitive values
+      if (a === a &&
+          !(a && objectTypes[type]) &&
+          !(b && objectTypes[otherType])) {
+        return false;
+      }
+      // exit early for `null` and `undefined` avoiding ES3's Function#call behavior
+      // http://es5.github.io/#x15.3.4.4
+      if (a == null || b == null) {
+        return a === b;
+      }
+      // compare [[Class]] names
+      var className = toString.call(a),
+          otherClass = toString.call(b);
+
+      if (className == argsClass) {
+        className = objectClass;
+      }
+      if (otherClass == argsClass) {
+        otherClass = objectClass;
+      }
+      if (className != otherClass) {
+        return false;
+      }
+      switch (className) {
+        case boolClass:
+        case dateClass:
+          // coerce dates and booleans to numbers, dates to milliseconds and booleans
+          // to `1` or `0` treating invalid dates coerced to `NaN` as not equal
+          return +a == +b;
+
+        case numberClass:
+          // treat `NaN` vs. `NaN` as equal
+          return (a != +a)
+            ? b != +b
+            // but treat `+0` vs. `-0` as not equal
+            : (a == 0 ? (1 / a == 1 / b) : a == +b);
+
+        case regexpClass:
+        case stringClass:
+          // coerce regexes to strings (http://es5.github.io/#x15.10.6.4)
+          // treat string primitives and their corresponding object instances as equal
+          return a == String(b);
+      }
+      var isArr = className == arrayClass;
+      if (!isArr) {
+        // unwrap any `lodash` wrapped values
+        var aWrapped = hasOwnProperty.call(a, '__wrapped__'),
+            bWrapped = hasOwnProperty.call(b, '__wrapped__');
+
+        if (aWrapped || bWrapped) {
+          return baseIsEqual(aWrapped ? a.__wrapped__ : a, bWrapped ? b.__wrapped__ : b, callback, isWhere, stackA, stackB);
+        }
+        // exit for functions and DOM nodes
+        if (className != objectClass) {
+          return false;
+        }
+        // in older versions of Opera, `arguments` objects have `Array` constructors
+        var ctorA = a.constructor,
+            ctorB = b.constructor;
+
+        // non `Object` object instances with different constructors are not equal
+        if (ctorA != ctorB &&
+              !(isFunction(ctorA) && ctorA instanceof ctorA && isFunction(ctorB) && ctorB instanceof ctorB) &&
+              ('constructor' in a && 'constructor' in b)
+            ) {
+          return false;
+        }
+      }
+      // assume cyclic structures are equal
+      // the algorithm for detecting cyclic structures is adapted from ES 5.1
+      // section 15.12.3, abstract operation `JO` (http://es5.github.io/#x15.12.3)
+      var initedStack = !stackA;
+      stackA || (stackA = getArray());
+      stackB || (stackB = getArray());
+
+      var length = stackA.length;
+      while (length--) {
+        if (stackA[length] == a) {
+          return stackB[length] == b;
+        }
+      }
+      var size = 0;
+      result = true;
+
+      // add `a` and `b` to the stack of traversed objects
+      stackA.push(a);
+      stackB.push(b);
+
+      // recursively compare objects and arrays (susceptible to call stack limits)
+      if (isArr) {
+        // compare lengths to determine if a deep comparison is necessary
+        length = a.length;
+        size = b.length;
+        result = size == length;
+
+        if (result || isWhere) {
+          // deep compare the contents, ignoring non-numeric properties
+          while (size--) {
+            var index = length,
+                value = b[size];
+
+            if (isWhere) {
+              while (index--) {
+                if ((result = baseIsEqual(a[index], value, callback, isWhere, stackA, stackB))) {
+                  break;
+                }
+              }
+            } else if (!(result = baseIsEqual(a[size], value, callback, isWhere, stackA, stackB))) {
+              break;
+            }
+          }
+        }
+      }
+      else {
+        // deep compare objects using `forIn`, instead of `forOwn`, to avoid `Object.keys`
+        // which, in this case, is more costly
+        forIn(b, function(value, key, b) {
+          if (hasOwnProperty.call(b, key)) {
+            // count the number of properties.
+            size++;
+            // deep compare each property value.
+            return (result = hasOwnProperty.call(a, key) && baseIsEqual(a[key], value, callback, isWhere, stackA, stackB));
+          }
+        });
+
+        if (result && !isWhere) {
+          // ensure both objects have the same number of properties
+          forIn(a, function(value, key, a) {
+            if (hasOwnProperty.call(a, key)) {
+              // `size` will be `-1` if `a` has more properties than `b`
+              return (result = --size > -1);
+            }
+          });
+        }
+      }
+      stackA.pop();
+      stackB.pop();
+
+      if (initedStack) {
+        releaseArray(stackA);
+        releaseArray(stackB);
+      }
+      return result;
+    }
+
+    /**
+     * The base implementation of `_.merge` without argument juggling or support
+     * for `thisArg` binding.
+     *
+     * @private
+     * @param {Object} object The destination object.
+     * @param {Object} source The source object.
+     * @param {Function} [callback] The function to customize merging properties.
+     * @param {Array} [stackA=[]] Tracks traversed source objects.
+     * @param {Array} [stackB=[]] Associates values with source counterparts.
+     */
+    function baseMerge(object, source, callback, stackA, stackB) {
+      (isArray(source) ? forEach : forOwn)(source, function(source, key) {
+        var found,
+            isArr,
+            result = source,
+            value = object[key];
+
+        if (source && ((isArr = isArray(source)) || isPlainObject(source))) {
+          // avoid merging previously merged cyclic sources
+          var stackLength = stackA.length;
+          while (stackLength--) {
+            if ((found = stackA[stackLength] == source)) {
+              value = stackB[stackLength];
+              break;
+            }
+          }
+          if (!found) {
+            var isShallow;
+            if (callback) {
+              result = callback(value, source);
+              if ((isShallow = typeof result != 'undefined')) {
+                value = result;
+              }
+            }
+            if (!isShallow) {
+              value = isArr
+                ? (isArray(value) ? value : [])
+                : (isPlainObject(value) ? value : {});
+            }
+            // add `source` and associated `value` to the stack of traversed objects
+            stackA.push(source);
+            stackB.push(value);
+
+            // recursively merge objects and arrays (susceptible to call stack limits)
+            if (!isShallow) {
+              baseMerge(value, source, callback, stackA, stackB);
+            }
+          }
+        }
+        else {
+          if (callback) {
+            result = callback(value, source);
+            if (typeof result == 'undefined') {
+              result = source;
+            }
+          }
+          if (typeof result != 'undefined') {
+            value = result;
+          }
+        }
+        object[key] = value;
+      });
+    }
+
+    /**
+     * The base implementation of `_.random` without argument juggling or support
+     * for returning floating-point numbers.
+     *
+     * @private
+     * @param {number} min The minimum possible value.
+     * @param {number} max The maximum possible value.
+     * @returns {number} Returns a random number.
+     */
+    function baseRandom(min, max) {
+      return min + floor(nativeRandom() * (max - min + 1));
+    }
+
+    /**
+     * The base implementation of `_.uniq` without support for callback shorthands
+     * or `thisArg` binding.
+     *
+     * @private
+     * @param {Array} array The array to process.
+     * @param {boolean} [isSorted=false] A flag to indicate that `array` is sorted.
+     * @param {Function} [callback] The function called per iteration.
+     * @returns {Array} Returns a duplicate-value-free array.
+     */
+    function baseUniq(array, isSorted, callback) {
+      var index = -1,
+          indexOf = getIndexOf(),
+          length = array ? array.length : 0,
+          result = [];
+
+      var isLarge = !isSorted && length >= largeArraySize && indexOf === baseIndexOf,
+          seen = (callback || isLarge) ? getArray() : result;
+
+      if (isLarge) {
+        var cache = createCache(seen);
+        indexOf = cacheIndexOf;
+        seen = cache;
+      }
+      while (++index < length) {
+        var value = array[index],
+            computed = callback ? callback(value, index, array) : value;
+
+        if (isSorted
+              ? !index || seen[seen.length - 1] !== computed
+              : indexOf(seen, computed) < 0
+            ) {
+          if (callback || isLarge) {
+            seen.push(computed);
+          }
+          result.push(value);
+        }
+      }
+      if (isLarge) {
+        releaseArray(seen.array);
+        releaseObject(seen);
+      } else if (callback) {
+        releaseArray(seen);
+      }
+      return result;
+    }
+
+    /**
+     * Creates a function that aggregates a collection, creating an object composed
+     * of keys generated from the results of running each element of the collection
+     * through a callback. The given `setter` function sets the keys and values
+     * of the composed object.
+     *
+     * @private
+     * @param {Function} setter The setter function.
+     * @returns {Function} Returns the new aggregator function.
+     */
+    function createAggregator(setter) {
+      return function(collection, callback, thisArg) {
+        var result = {};
+        callback = lodash.createCallback(callback, thisArg, 3);
+
+        var index = -1,
+            length = collection ? collection.length : 0;
+
+        if (typeof length == 'number') {
+          while (++index < length) {
+            var value = collection[index];
+            setter(result, value, callback(value, index, collection), collection);
+          }
+        } else {
+          forOwn(collection, function(value, key, collection) {
+            setter(result, value, callback(value, key, collection), collection);
+          });
+        }
+        return result;
+      };
+    }
+
+    /**
+     * Creates a function that, when called, either curries or invokes `func`
+     * with an optional `this` binding and partially applied arguments.
+     *
+     * @private
+     * @param {Function|string} func The function or method name to reference.
+     * @param {number} bitmask The bitmask of method flags to compose.
+     *  The bitmask may be composed of the following flags:
+     *  1 - `_.bind`
+     *  2 - `_.bindKey`
+     *  4 - `_.curry`
+     *  8 - `_.curry` (bound)
+     *  16 - `_.partial`
+     *  32 - `_.partialRight`
+     * @param {Array} [partialArgs] An array of arguments to prepend to those
+     *  provided to the new function.
+     * @param {Array} [partialRightArgs] An array of arguments to append to those
+     *  provided to the new function.
+     * @param {*} [thisArg] The `this` binding of `func`.
+     * @param {number} [arity] The arity of `func`.
+     * @returns {Function} Returns the new function.
+     */
+    function createWrapper(func, bitmask, partialArgs, partialRightArgs, thisArg, arity) {
+      var isBind = bitmask & 1,
+          isBindKey = bitmask & 2,
+          isCurry = bitmask & 4,
+          isCurryBound = bitmask & 8,
+          isPartial = bitmask & 16,
+          isPartialRight = bitmask & 32;
+
+      if (!isBindKey && !isFunction(func)) {
+        throw new TypeError;
+      }
+      if (isPartial && !partialArgs.length) {
+        bitmask &= ~16;
+        isPartial = partialArgs = false;
+      }
+      if (isPartialRight && !partialRightArgs.length) {
+        bitmask &= ~32;
+        isPartialRight = partialRightArgs = false;
+      }
+      var bindData = func && func.__bindData__;
+      if (bindData && bindData !== true) {
+        // clone `bindData`
+        bindData = slice(bindData);
+        if (bindData[2]) {
+          bindData[2] = slice(bindData[2]);
+        }
+        if (bindData[3]) {
+          bindData[3] = slice(bindData[3]);
+        }
+        // set `thisBinding` is not previously bound
+        if (isBind && !(bindData[1] & 1)) {
+          bindData[4] = thisArg;
+        }
+        // set if previously bound but not currently (subsequent curried functions)
+        if (!isBind && bindData[1] & 1) {
+          bitmask |= 8;
+        }
+        // set curried arity if not yet set
+        if (isCurry && !(bindData[1] & 4)) {
+          bindData[5] = arity;
+        }
+        // append partial left arguments
+        if (isPartial) {
+          push.apply(bindData[2] || (bindData[2] = []), partialArgs);
+        }
+        // append partial right arguments
+        if (isPartialRight) {
+          unshift.apply(bindData[3] || (bindData[3] = []), partialRightArgs);
+        }
+        // merge flags
+        bindData[1] |= bitmask;
+        return createWrapper.apply(null, bindData);
+      }
+      // fast path for `_.bind`
+      var creater = (bitmask == 1 || bitmask === 17) ? baseBind : baseCreateWrapper;
+      return creater([func, bitmask, partialArgs, partialRightArgs, thisArg, arity]);
+    }
+
+    /**
+     * Used by `escape` to convert characters to HTML entities.
+     *
+     * @private
+     * @param {string} match The matched character to escape.
+     * @returns {string} Returns the escaped character.
+     */
+    function escapeHtmlChar(match) {
+      return htmlEscapes[match];
+    }
+
+    /**
+     * Gets the appropriate "indexOf" function. If the `_.indexOf` method is
+     * customized, this method returns the custom method, otherwise it returns
+     * the `baseIndexOf` function.
+     *
+     * @private
+     * @returns {Function} Returns the "indexOf" function.
+     */
+    function getIndexOf() {
+      var result = (result = lodash.indexOf) === indexOf ? baseIndexOf : result;
+      return result;
+    }
+
+    /**
+     * Checks if `value` is a native function.
+     *
+     * @private
+     * @param {*} value The value to check.
+     * @returns {boolean} Returns `true` if the `value` is a native function, else `false`.
+     */
+    function isNative(value) {
+      return typeof value == 'function' && reNative.test(value);
+    }
+
+    /**
+     * Sets `this` binding data on a given function.
+     *
+     * @private
+     * @param {Function} func The function to set data on.
+     * @param {Array} value The data array to set.
+     */
+    var setBindData = !defineProperty ? noop : function(func, value) {
+      descriptor.value = value;
+      defineProperty(func, '__bindData__', descriptor);
+    };
+
+    /**
+     * A fallback implementation of `isPlainObject` which checks if a given value
+     * is an object created by the `Object` constructor, assuming objects created
+     * by the `Object` constructor have no inherited enumerable properties and that
+     * there are no `Object.prototype` extensions.
+     *
+     * @private
+     * @param {*} value The value to check.
+     * @returns {boolean} Returns `true` if `value` is a plain object, else `false`.
+     */
+    function shimIsPlainObject(value) {
+      var ctor,
+          result;
+
+      // avoid non Object objects, `arguments` objects, and DOM elements
+      if (!(value && toString.call(value) == objectClass) ||
+          (ctor = value.constructor, isFunction(ctor) && !(ctor instanceof ctor))) {
+        return false;
+      }
+      // In most environments an object's own properties are iterated before
+      // its inherited properties. If the last iterated property is an object's
+      // own property then there are no inherited enumerable properties.
+      forIn(value, function(value, key) {
+        result = key;
+      });
+      return typeof result == 'undefined' || hasOwnProperty.call(value, result);
+    }
+
+    /**
+     * Used by `unescape` to convert HTML entities to characters.
+     *
+     * @private
+     * @param {string} match The matched character to unescape.
+     * @returns {string} Returns the unescaped character.
+     */
+    function unescapeHtmlChar(match) {
+      return htmlUnescapes[match];
+    }
+
+    /*--------------------------------------------------------------------------*/
+
+    /**
+     * Checks if `value` is an `arguments` object.
+     *
+     * @static
+     * @memberOf _
+     * @category Objects
+     * @param {*} value The value to check.
+     * @returns {boolean} Returns `true` if the `value` is an `arguments` object, else `false`.
+     * @example
+     *
+     * (function() { return _.isArguments(arguments); })(1, 2, 3);
+     * // => true
+     *
+     * _.isArguments([1, 2, 3]);
+     * // => false
+     */
+    function isArguments(value) {
+      return value && typeof value == 'object' && typeof value.length == 'number' &&
+        toString.call(value) == argsClass || false;
+    }
+
+    /**
+     * Checks if `value` is an array.
+     *
+     * @static
+     * @memberOf _
+     * @type Function
+     * @category Objects
+     * @param {*} value The value to check.
+     * @returns {boolean} Returns `true` if the `value` is an array, else `false`.
+     * @example
+     *
+     * (function() { return _.isArray(arguments); })();
+     * // => false
+     *
+     * _.isArray([1, 2, 3]);
+     * // => true
+     */
+    var isArray = nativeIsArray || function(value) {
+      return value && typeof value == 'object' && typeof value.length == 'number' &&
+        toString.call(value) == arrayClass || false;
+    };
+
+    /**
+     * A fallback implementation of `Object.keys` which produces an array of the
+     * given object's own enumerable property names.
+     *
+     * @private
+     * @type Function
+     * @param {Object} object The object to inspect.
+     * @returns {Array} Returns an array of property names.
+     */
+    var shimKeys = function(object) {
+      var index, iterable = object, result = [];
+      if (!iterable) return result;
+      if (!(objectTypes[typeof object])) return result;
+        for (index in iterable) {
+          if (hasOwnProperty.call(iterable, index)) {
+            result.push(index);
+          }
+        }
+      return result
+    };
+
+    /**
+     * Creates an array composed of the own enumerable property names of an object.
+     *
+     * @static
+     * @memberOf _
+     * @category Objects
+     * @param {Object} object The object to inspect.
+     * @returns {Array} Returns an array of property names.
+     * @example
+     *
+     * _.keys({ 'one': 1, 'two': 2, 'three': 3 });
+     * // => ['one', 'two', 'three'] (property order is not guaranteed across environments)
+     */
+    var keys = !nativeKeys ? shimKeys : function(object) {
+      if (!isObject(object)) {
+        return [];
+      }
+      return nativeKeys(object);
+    };
+
+    /**
+     * Used to convert characters to HTML entities:
+     *
+     * Though the `>` character is escaped for symmetry, characters like `>` and `/`
+     * don't require escaping in HTML and have no special meaning unless they're part
+     * of a tag or an unquoted attribute value.
+     * http://mathiasbynens.be/notes/ambiguous-ampersands (under "semi-related fun fact")
+     */
+    var htmlEscapes = {
+      '&': '&amp;',
+      '<': '&lt;',
+      '>': '&gt;',
+      '"': '&quot;',
+      "'": '&#39;'
+    };
+
+    /** Used to convert HTML entities to characters */
+    var htmlUnescapes = invert(htmlEscapes);
+
+    /** Used to match HTML entities and HTML characters */
+    var reEscapedHtml = RegExp('(' + keys(htmlUnescapes).join('|') + ')', 'g'),
+        reUnescapedHtml = RegExp('[' + keys(htmlEscapes).join('') + ']', 'g');
+
+    /*--------------------------------------------------------------------------*/
+
+    /**
+     * Assigns own enumerable properties of source object(s) to the destination
+     * object. Subsequent sources will overwrite property assignments of previous
+     * sources. If a callback is provided it will be executed to produce the
+     * assigned values. The callback is bound to `thisArg` and invoked with two
+     * arguments; (objectValue, sourceValue).
+     *
+     * @static
+     * @memberOf _
+     * @type Function
+     * @alias extend
+     * @category Objects
+     * @param {Object} object The destination object.
+     * @param {...Object} [source] The source objects.
+     * @param {Function} [callback] The function to customize assigning values.
+     * @param {*} [thisArg] The `this` binding of `callback`.
+     * @returns {Object} Returns the destination object.
+     * @example
+     *
+     * _.assign({ 'name': 'fred' }, { 'employer': 'slate' });
+     * // => { 'name': 'fred', 'employer': 'slate' }
+     *
+     * var defaults = _.partialRight(_.assign, function(a, b) {
+     *   return typeof a == 'undefined' ? b : a;
+     * });
+     *
+     * var object = { 'name': 'barney' };
+     * defaults(object, { 'name': 'fred', 'employer': 'slate' });
+     * // => { 'name': 'barney', 'employer': 'slate' }
+     */
+    var assign = function(object, source, guard) {
+      var index, iterable = object, result = iterable;
+      if (!iterable) return result;
+      var args = arguments,
+          argsIndex = 0,
+          argsLength = typeof guard == 'number' ? 2 : args.length;
+      if (argsLength > 3 && typeof args[argsLength - 2] == 'function') {
+        var callback = baseCreateCallback(args[--argsLength - 1], args[argsLength--], 2);
+      } else if (argsLength > 2 && typeof args[argsLength - 1] == 'function') {
+        callback = args[--argsLength];
+      }
+      while (++argsIndex < argsLength) {
+        iterable = args[argsIndex];
+        if (iterable && objectTypes[typeof iterable]) {
+        var ownIndex = -1,
+            ownProps = objectTypes[typeof iterable] && keys(iterable),
+            length = ownProps ? ownProps.length : 0;
+
+        while (++ownIndex < length) {
+          index = ownProps[ownIndex];
+          result[index] = callback ? callback(result[index], iterable[index]) : iterable[index];
+        }
+        }
+      }
+      return result
+    };
+
+    /**
+     * Creates a clone of `value`. If `isDeep` is `true` nested objects will also
+     * be cloned, otherwise they will be assigned by reference. If a callback
+     * is provided it will be executed to produce the cloned values. If the
+     * callback returns `undefined` cloning will be handled by the method instead.
+     * The callback is bound to `thisArg` and invoked with one argument; (value).
+     *
+     * @static
+     * @memberOf _
+     * @category Objects
+     * @param {*} value The value to clone.
+     * @param {boolean} [isDeep=false] Specify a deep clone.
+     * @param {Function} [callback] The function to customize cloning values.
+     * @param {*} [thisArg] The `this` binding of `callback`.
+     * @returns {*} Returns the cloned value.
+     * @example
+     *
+     * var characters = [
+     *   { 'name': 'barney', 'age': 36 },
+     *   { 'name': 'fred',   'age': 40 }
+     * ];
+     *
+     * var shallow = _.clone(characters);
+     * shallow[0] === characters[0];
+     * // => true
+     *
+     * var deep = _.clone(characters, true);
+     * deep[0] === characters[0];
+     * // => false
+     *
+     * _.mixin({
+     *   'clone': _.partialRight(_.clone, function(value) {
+     *     return _.isElement(value) ? value.cloneNode(false) : undefined;
+     *   })
+     * });
+     *
+     * var clone = _.clone(document.body);
+     * clone.childNodes.length;
+     * // => 0
+     */
+    function clone(value, isDeep, callback, thisArg) {
+      // allows working with "Collections" methods without using their `index`
+      // and `collection` arguments for `isDeep` and `callback`
+      if (typeof isDeep != 'boolean' && isDeep != null) {
+        thisArg = callback;
+        callback = isDeep;
+        isDeep = false;
+      }
+      return baseClone(value, isDeep, typeof callback == 'function' && baseCreateCallback(callback, thisArg, 1));
+    }
+
+    /**
+     * Creates a deep clone of `value`. If a callback is provided it will be
+     * executed to produce the cloned values. If the callback returns `undefined`
+     * cloning will be handled by the method instead. The callback is bound to
+     * `thisArg` and invoked with one argument; (value).
+     *
+     * Note: This method is loosely based on the structured clone algorithm. Functions
+     * and DOM nodes are **not** cloned. The enumerable properties of `arguments` objects and
+     * objects created by constructors other than `Object` are cloned to plain `Object` objects.
+     * See http://www.w3.org/TR/html5/infrastructure.html#internal-structured-cloning-algorithm.
+     *
+     * @static
+     * @memberOf _
+     * @category Objects
+     * @param {*} value The value to deep clone.
+     * @param {Function} [callback] The function to customize cloning values.
+     * @param {*} [thisArg] The `this` binding of `callback`.
+     * @returns {*} Returns the deep cloned value.
+     * @example
+     *
+     * var characters = [
+     *   { 'name': 'barney', 'age': 36 },
+     *   { 'name': 'fred',   'age': 40 }
+     * ];
+     *
+     * var deep = _.cloneDeep(characters);
+     * deep[0] === characters[0];
+     * // => false
+     *
+     * var view = {
+     *   'label': 'docs',
+     *   'node': element
+     * };
+     *
+     * var clone = _.cloneDeep(view, function(value) {
+     *   return _.isElement(value) ? value.cloneNode(true) : undefined;
+     * });
+     *
+     * clone.node == view.node;
+     * // => false
+     */
+    function cloneDeep(value, callback, thisArg) {
+      return baseClone(value, true, typeof callback == 'function' && baseCreateCallback(callback, thisArg, 1));
+    }
+
+    /**
+     * Creates an object that inherits from the given `prototype` object. If a
+     * `properties` object is provided its own enumerable properties are assigned
+     * to the created object.
+     *
+     * @static
+     * @memberOf _
+     * @category Objects
+     * @param {Object} prototype The object to inherit from.
+     * @param {Object} [properties] The properties to assign to the object.
+     * @returns {Object} Returns the new object.
+     * @example
+     *
+     * function Shape() {
+     *   this.x = 0;
+     *   this.y = 0;
+     * }
+     *
+     * function Circle() {
+     *   Shape.call(this);
+     * }
+     *
+     * Circle.prototype = _.create(Shape.prototype, { 'constructor': Circle });
+     *
+     * var circle = new Circle;
+     * circle instanceof Circle;
+     * // => true
+     *
+     * circle instanceof Shape;
+     * // => true
+     */
+    function create(prototype, properties) {
+      var result = baseCreate(prototype);
+      return properties ? assign(result, properties) : result;
+    }
+
+    /**
+     * Assigns own enumerable properties of source object(s) to the destination
+     * object for all destination properties that resolve to `undefined`. Once a
+     * property is set, additional defaults of the same property will be ignored.
+     *
+     * @static
+     * @memberOf _
+     * @type Function
+     * @category Objects
+     * @param {Object} object The destination object.
+     * @param {...Object} [source] The source objects.
+     * @param- {Object} [guard] Allows working with `_.reduce` without using its
+     *  `key` and `object` arguments as sources.
+     * @returns {Object} Returns the destination object.
+     * @example
+     *
+     * var object = { 'name': 'barney' };
+     * _.defaults(object, { 'name': 'fred', 'employer': 'slate' });
+     * // => { 'name': 'barney', 'employer': 'slate' }
+     */
+    var defaults = function(object, source, guard) {
+      var index, iterable = object, result = iterable;
+      if (!iterable) return result;
+      var args = arguments,
+          argsIndex = 0,
+          argsLength = typeof guard == 'number' ? 2 : args.length;
+      while (++argsIndex < argsLength) {
+        iterable = args[argsIndex];
+        if (iterable && objectTypes[typeof iterable]) {
+        var ownIndex = -1,
+            ownProps = objectTypes[typeof iterable] && keys(iterable),
+            length = ownProps ? ownProps.length : 0;
+
+        while (++ownIndex < length) {
+          index = ownProps[ownIndex];
+          if (typeof result[index] == 'undefined') result[index] = iterable[index];
+        }
+        }
+      }
+      return result
+    };
+
+    /**
+     * This method is like `_.findIndex` except that it returns the key of the
+     * first element that passes the callback check, instead of the element itself.
+     *
+     * If a property name is provided for `callback` the created "_.pluck" style
+     * callback will return the property value of the given element.
+     *
+     * If an object is provided for `callback` the created "_.where" style callback
+     * will return `true` for elements that have the properties of the given object,
+     * else `false`.
+     *
+     * @static
+     * @memberOf _
+     * @category Objects
+     * @param {Object} object The object to search.
+     * @param {Function|Object|string} [callback=identity] The function called per
+     *  iteration. If a property name or object is provided it will be used to
+     *  create a "_.pluck" or "_.where" style callback, respectively.
+     * @param {*} [thisArg] The `this` binding of `callback`.
+     * @returns {string|undefined} Returns the key of the found element, else `undefined`.
+     * @example
+     *
+     * var characters = {
+     *   'barney': {  'age': 36, 'blocked': false },
+     *   'fred': {    'age': 40, 'blocked': true },
+     *   'pebbles': { 'age': 1,  'blocked': false }
+     * };
+     *
+     * _.findKey(characters, function(chr) {
+     *   return chr.age < 40;
+     * });
+     * // => 'barney' (property order is not guaranteed across environments)
+     *
+     * // using "_.where" callback shorthand
+     * _.findKey(characters, { 'age': 1 });
+     * // => 'pebbles'
+     *
+     * // using "_.pluck" callback shorthand
+     * _.findKey(characters, 'blocked');
+     * // => 'fred'
+     */
+    function findKey(object, callback, thisArg) {
+      var result;
+      callback = lodash.createCallback(callback, thisArg, 3);
+      forOwn(object, function(value, key, object) {
+        if (callback(value, key, object)) {
+          result = key;
+          return false;
+        }
+      });
+      return result;
+    }
+
+    /**
+     * This method is like `_.findKey` except that it iterates over elements
+     * of a `collection` in the opposite order.
+     *
+     * If a property name is provided for `callback` the created "_.pluck" style
+     * callback will return the property value of the given element.
+     *
+     * If an object is provided for `callback` the created "_.where" style callback
+     * will return `true` for elements that have the properties of the given object,
+     * else `false`.
+     *
+     * @static
+     * @memberOf _
+     * @category Objects
+     * @param {Object} object The object to search.
+     * @param {Function|Object|string} [callback=identity] The function called per
+     *  iteration. If a property name or object is provided it will be used to
+     *  create a "_.pluck" or "_.where" style callback, respectively.
+     * @param {*} [thisArg] The `this` binding of `callback`.
+     * @returns {string|undefined} Returns the key of the found element, else `undefined`.
+     * @example
+     *
+     * var characters = {
+     *   'barney': {  'age': 36, 'blocked': true },
+     *   'fred': {    'age': 40, 'blocked': false },
+     *   'pebbles': { 'age': 1,  'blocked': true }
+     * };
+     *
+     * _.findLastKey(characters, function(chr) {
+     *   return chr.age < 40;
+     * });
+     * // => returns `pebbles`, assuming `_.findKey` returns `barney`
+     *
+     * // using "_.where" callback shorthand
+     * _.findLastKey(characters, { 'age': 40 });
+     * // => 'fred'
+     *
+     * // using "_.pluck" callback shorthand
+     * _.findLastKey(characters, 'blocked');
+     * // => 'pebbles'
+     */
+    function findLastKey(object, callback, thisArg) {
+      var result;
+      callback = lodash.createCallback(callback, thisArg, 3);
+      forOwnRight(object, function(value, key, object) {
+        if (callback(value, key, object)) {
+          result = key;
+          return false;
+        }
+      });
+      return result;
+    }
+
+    /**
+     * Iterates over own and inherited enumerable properties of an object,
+     * executing the callback for each property. The callback is bound to `thisArg`
+     * and invoked with three arguments; (value, key, object). Callbacks may exit
+     * iteration early by explicitly returning `false`.
+     *
+     * @static
+     * @memberOf _
+     * @type Function
+     * @category Objects
+     * @param {Object} object The object to iterate over.
+     * @param {Function} [callback=identity] The function called per iteration.
+     * @param {*} [thisArg] The `this` binding of `callback`.
+     * @returns {Object} Returns `object`.
+     * @example
+     *
+     * function Shape() {
+     *   this.x = 0;
+     *   this.y = 0;
+     * }
+     *
+     * Shape.prototype.move = function(x, y) {
+     *   this.x += x;
+     *   this.y += y;
+     * };
+     *
+     * _.forIn(new Shape, function(value, key) {
+     *   console.log(key);
+     * });
+     * // => logs 'x', 'y', and 'move' (property order is not guaranteed across environments)
+     */
+    var forIn = function(collection, callback, thisArg) {
+      var index, iterable = collection, result = iterable;
+      if (!iterable) return result;
+      if (!objectTypes[typeof iterable]) return result;
+      callback = callback && typeof thisArg == 'undefined' ? callback : baseCreateCallback(callback, thisArg, 3);
+        for (index in iterable) {
+          if (callback(iterable[index], index, collection) === false) return result;
+        }
+      return result
+    };
+
+    /**
+     * This method is like `_.forIn` except that it iterates over elements
+     * of a `collection` in the opposite order.
+     *
+     * @static
+     * @memberOf _
+     * @category Objects
+     * @param {Object} object The object to iterate over.
+     * @param {Function} [callback=identity] The function called per iteration.
+     * @param {*} [thisArg] The `this` binding of `callback`.
+     * @returns {Object} Returns `object`.
+     * @example
+     *
+     * function Shape() {
+     *   this.x = 0;
+     *   this.y = 0;
+     * }
+     *
+     * Shape.prototype.move = function(x, y) {
+     *   this.x += x;
+     *   this.y += y;
+     * };
+     *
+     * _.forInRight(new Shape, function(value, key) {
+     *   console.log(key);
+     * });
+     * // => logs 'move', 'y', and 'x' assuming `_.forIn ` logs 'x', 'y', and 'move'
+     */
+    function forInRight(object, callback, thisArg) {
+      var pairs = [];
+
+      forIn(object, function(value, key) {
+        pairs.push(key, value);
+      });
+
+      var length = pairs.length;
+      callback = baseCreateCallback(callback, thisArg, 3);
+      while (length--) {
+        if (callback(pairs[length--], pairs[length], object) === false) {
+          break;
+        }
+      }
+      return object;
+    }
+
+    /**
+     * Iterates over own enumerable properties of an object, executing the callback
+     * for each property. The callback is bound to `thisArg` and invoked with three
+     * arguments; (value, key, object). Callbacks may exit iteration early by
+     * explicitly returning `false`.
+     *
+     * @static
+     * @memberOf _
+     * @type Function
+     * @category Objects
+     * @param {Object} object The object to iterate over.
+     * @param {Function} [callback=identity] The function called per iteration.
+     * @param {*} [thisArg] The `this` binding of `callback`.
+     * @returns {Object} Returns `object`.
+     * @example
+     *
+     * _.forOwn({ '0': 'zero', '1': 'one', 'length': 2 }, function(num, key) {
+     *   console.log(key);
+     * });
+     * // => logs '0', '1', and 'length' (property order is not guaranteed across environments)
+     */
+    var forOwn = function(collection, callback, thisArg) {
+      var index, iterable = collection, result = iterable;
+      if (!iterable) return result;
+      if (!objectTypes[typeof iterable]) return result;
+      callback = callback && typeof thisArg == 'undefined' ? callback : baseCreateCallback(callback, thisArg, 3);
+        var ownIndex = -1,
+            ownProps = objectTypes[typeof iterable] && keys(iterable),
+            length = ownProps ? ownProps.length : 0;
+
+        while (++ownIndex < length) {
+          index = ownProps[ownIndex];
+          if (callback(iterable[index], index, collection) === false) return result;
+        }
+      return result
+    };
+
+    /**
+     * This method is like `_.forOwn` except that it iterates over elements
+     * of a `collection` in the opposite order.
+     *
+     * @static
+     * @memberOf _
+     * @category Objects
+     * @param {Object} object The object to iterate over.
+     * @param {Function} [callback=identity] The function called per iteration.
+     * @param {*} [thisArg] The `this` binding of `callback`.
+     * @returns {Object} Returns `object`.
+     * @example
+     *
+     * _.forOwnRight({ '0': 'zero', '1': 'one', 'length': 2 }, function(num, key) {
+     *   console.log(key);
+     * });
+     * // => logs 'length', '1', and '0' assuming `_.forOwn` logs '0', '1', and 'length'
+     */
+    function forOwnRight(object, callback, thisArg) {
+      var props = keys(object),
+          length = props.length;
+
+      callback = baseCreateCallback(callback, thisArg, 3);
+      while (length--) {
+        var key = props[length];
+        if (callback(object[key], key, object) === false) {
+          break;
+        }
+      }
+      return object;
+    }
+
+    /**
+     * Creates a sorted array of property names of all enumerable properties,
+     * own and inherited, of `object` that have function values.
+     *
+     * @static
+     * @memberOf _
+     * @alias methods
+     * @category Objects
+     * @param {Object} object The object to inspect.
+     * @returns {Array} Returns an array of property names that have function values.
+     * @example
+     *
+     * _.functions(_);
+     * // => ['all', 'any', 'bind', 'bindAll', 'clone', 'compact', 'compose', ...]
+     */
+    function functions(object) {
+      var result = [];
+      forIn(object, function(value, key) {
+        if (isFunction(value)) {
+          result.push(key);
+        }
+      });
+      return result.sort();
+    }
+
+    /**
+     * Checks if the specified property name exists as a direct property of `object`,
+     * instead of an inherited property.
+     *
+     * @static
+     * @memberOf _
+     * @category Objects
+     * @param {Object} object The object to inspect.
+     * @param {string} key The name of the property to check.
+     * @returns {boolean} Returns `true` if key is a direct property, else `false`.
+     * @example
+     *
+     * _.has({ 'a': 1, 'b': 2, 'c': 3 }, 'b');
+     * // => true
+     */
+    function has(object, key) {
+      return object ? hasOwnProperty.call(object, key) : false;
+    }
+
+    /**
+     * Creates an object composed of the inverted keys and values of the given object.
+     *
+     * @static
+     * @memberOf _
+     * @category Objects
+     * @param {Object} object The object to invert.
+     * @returns {Object} Returns the created inverted object.
+     * @example
+     *
+     * _.invert({ 'first': 'fred', 'second': 'barney' });
+     * // => { 'fred': 'first', 'barney': 'second' }
+     */
+    function invert(object) {
+      var index = -1,
+          props = keys(object),
+          length = props.length,
+          result = {};
+
+      while (++index < length) {
+        var key = props[index];
+        result[object[key]] = key;
+      }
+      return result;
+    }
+
+    /**
+     * Checks if `value` is a boolean value.
+     *
+     * @static
+     * @memberOf _
+     * @category Objects
+     * @param {*} value The value to check.
+     * @returns {boolean} Returns `true` if the `value` is a boolean value, else `false`.
+     * @example
+     *
+     * _.isBoolean(null);
+     * // => false
+     */
+    function isBoolean(value) {
+      return value === true || value === false ||
+        value && typeof value == 'object' && toString.call(value) == boolClass || false;
+    }
+
+    /**
+     * Checks if `value` is a date.
+     *
+     * @static
+     * @memberOf _
+     * @category Objects
+     * @param {*} value The value to check.
+     * @returns {boolean} Returns `true` if the `value` is a date, else `false`.
+     * @example
+     *
+     * _.isDate(new Date);
+     * // => true
+     */
+    function isDate(value) {
+      return value && typeof value == 'object' && toString.call(value) == dateClass || false;
+    }
+
+    /**
+     * Checks if `value` is a DOM element.
+     *
+     * @static
+     * @memberOf _
+     * @category Objects
+     * @param {*} value The value to check.
+     * @returns {boolean} Returns `true` if the `value` is a DOM element, else `false`.
+     * @example
+     *
+     * _.isElement(document.body);
+     * // => true
+     */
+    function isElement(value) {
+      return value && value.nodeType === 1 || false;
+    }
+
+    /**
+     * Checks if `value` is empty. Arrays, strings, or `arguments` objects with a
+     * length of `0` and objects with no own enumerable properties are considered
+     * "empty".
+     *
+     * @static
+     * @memberOf _
+     * @category Objects
+     * @param {Array|Object|string} value The value to inspect.
+     * @returns {boolean} Returns `true` if the `value` is empty, else `false`.
+     * @example
+     *
+     * _.isEmpty([1, 2, 3]);
+     * // => false
+     *
+     * _.isEmpty({});
+     * // => true
+     *
+     * _.isEmpty('');
+     * // => true
+     */
+    function isEmpty(value) {
+      var result = true;
+      if (!value) {
+        return result;
+      }
+      var className = toString.call(value),
+          length = value.length;
+
+      if ((className == arrayClass || className == stringClass || className == argsClass ) ||
+          (className == objectClass && typeof length == 'number' && isFunction(value.splice))) {
+        return !length;
+      }
+      forOwn(value, function() {
+        return (result = false);
+      });
+      return result;
+    }
+
+    /**
+     * Performs a deep comparison between two values to determine if they are
+     * equivalent to each other. If a callback is provided it will be executed
+     * to compare values. If the callback returns `undefined` comparisons will
+     * be handled by the method instead. The callback is bound to `thisArg` and
+     * invoked with two arguments; (a, b).
+     *
+     * @static
+     * @memberOf _
+     * @category Objects
+     * @param {*} a The value to compare.
+     * @param {*} b The other value to compare.
+     * @param {Function} [callback] The function to customize comparing values.
+     * @param {*} [thisArg] The `this` binding of `callback`.
+     * @returns {boolean} Returns `true` if the values are equivalent, else `false`.
+     * @example
+     *
+     * var object = { 'name': 'fred' };
+     * var copy = { 'name': 'fred' };
+     *
+     * object == copy;
+     * // => false
+     *
+     * _.isEqual(object, copy);
+     * // => true
+     *
+     * var words = ['hello', 'goodbye'];
+     * var otherWords = ['hi', 'goodbye'];
+     *
+     * _.isEqual(words, otherWords, function(a, b) {
+     *   var reGreet = /^(?:hello|hi)$/i,
+     *       aGreet = _.isString(a) && reGreet.test(a),
+     *       bGreet = _.isString(b) && reGreet.test(b);
+     *
+     *   return (aGreet || bGreet) ? (aGreet == bGreet) : undefined;
+     * });
+     * // => true
+     */
+    function isEqual(a, b, callback, thisArg) {
+      return baseIsEqual(a, b, typeof callback == 'function' && baseCreateCallback(callback, thisArg, 2));
+    }
+
+    /**
+     * Checks if `value` is, or can be coerced to, a finite number.
+     *
+     * Note: This is not the same as native `isFinite` which will return true for
+     * booleans and empty strings. See http://es5.github.io/#x15.1.2.5.
+     *
+     * @static
+     * @memberOf _
+     * @category Objects
+     * @param {*} value The value to check.
+     * @returns {boolean} Returns `true` if the `value` is finite, else `false`.
+     * @example
+     *
+     * _.isFinite(-101);
+     * // => true
+     *
+     * _.isFinite('10');
+     * // => true
+     *
+     * _.isFinite(true);
+     * // => false
+     *
+     * _.isFinite('');
+     * // => false
+     *
+     * _.isFinite(Infinity);
+     * // => false
+     */
+    function isFinite(value) {
+      return nativeIsFinite(value) && !nativeIsNaN(parseFloat(value));
+    }
+
+    /**
+     * Checks if `value` is a function.
+     *
+     * @static
+     * @memberOf _
+     * @category Objects
+     * @param {*} value The value to check.
+     * @returns {boolean} Returns `true` if the `value` is a function, else `false`.
+     * @example
+     *
+     * _.isFunction(_);
+     * // => true
+     */
+    function isFunction(value) {
+      return typeof value == 'function';
+    }
+
+    /**
+     * Checks if `value` is the language type of Object.
+     * (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)
+     *
+     * @static
+     * @memberOf _
+     * @category Objects
+     * @param {*} value The value to check.
+     * @returns {boolean} Returns `true` if the `value` is an object, else `false`.
+     * @example
+     *
+     * _.isObject({});
+     * // => true
+     *
+     * _.isObject([1, 2, 3]);
+     * // => true
+     *
+     * _.isObject(1);
+     * // => false
+     */
+    function isObject(value) {
+      // check if the value is the ECMAScript language type of Object
+      // http://es5.github.io/#x8
+      // and avoid a V8 bug
+      // http://code.google.com/p/v8/issues/detail?id=2291
+      return !!(value && objectTypes[typeof value]);
+    }
+
+    /**
+     * Checks if `value` is `NaN`.
+     *
+     * Note: This is not the same as native `isNaN` which will return `true` for
+     * `undefined` and other non-numeric values. See http://es5.github.io/#x15.1.2.4.
+     *
+     * @static
+     * @memberOf _
+     * @category Objects
+     * @param {*} value The value to check.
+     * @returns {boolean} Returns `true` if the `value` is `NaN`, else `false`.
+     * @example
+     *
+     * _.isNaN(NaN);
+     * // => true
+     *
+     * _.isNaN(new Number(NaN));
+     * // => true
+     *
+     * isNaN(undefined);
+     * // => true
+     *
+     * _.isNaN(undefined);
+     * // => false
+     */
+    function isNaN(value) {
+      // `NaN` as a primitive is the only value that is not equal to itself
+      // (perform the [[Class]] check first to avoid errors with some host objects in IE)
+      return isNumber(value) && value != +value;
+    }
+
+    /**
+     * Checks if `value` is `null`.
+     *
+     * @static
+     * @memberOf _
+     * @category Objects
+     * @param {*} value The value to check.
+     * @returns {boolean} Returns `true` if the `value` is `null`, else `false`.
+     * @example
+     *
+     * _.isNull(null);
+     * // => true
+     *
+     * _.isNull(undefined);
+     * // => false
+     */
+    function isNull(value) {
+      return value === null;
+    }
+
+    /**
+     * Checks if `value` is a number.
+     *
+     * Note: `NaN` is considered a number. See http://es5.github.io/#x8.5.
+     *
+     * @static
+     * @memberOf _
+     * @category Objects
+     * @param {*} value The value to check.
+     * @returns {boolean} Returns `true` if the `value` is a number, else `false`.
+     * @example
+     *
+     * _.isNumber(8.4 * 5);
+     * // => true
+     */
+    function isNumber(value) {
+      return typeof value == 'number' ||
+        value && typeof value == 'object' && toString.call(value) == numberClass || false;
+    }
+
+    /**
+     * Checks if `value` is an object created by the `Object` constructor.
+     *
+     * @static
+     * @memberOf _
+     * @category Objects
+     * @param {*} value The value to check.
+     * @returns {boolean} Returns `true` if `value` is a plain object, else `false`.
+     * @example
+     *
+     * function Shape() {
+     *   this.x = 0;
+     *   this.y = 0;
+     * }
+     *
+     * _.isPlainObject(new Shape);
+     * // => false
+     *
+     * _.isPlainObject([1, 2, 3]);
+     * // => false
+     *
+     * _.isPlainObject({ 'x': 0, 'y': 0 });
+     * // => true
+     */
+    var isPlainObject = !getPrototypeOf ? shimIsPlainObject : function(value) {
+      if (!(value && toString.call(value) == objectClass)) {
+        return false;
+      }
+      var valueOf = value.valueOf,
+          objProto = isNative(valueOf) && (objProto = getPrototypeOf(valueOf)) && getPrototypeOf(objProto);
+
+      return objProto
+        ? (value == objProto || getPrototypeOf(value) == objProto)
+        : shimIsPlainObject(value);
+    };
+
+    /**
+     * Checks if `value` is a regular expression.
+     *
+     * @static
+     * @memberOf _
+     * @category Objects
+     * @param {*} value The value to check.
+     * @returns {boolean} Returns `true` if the `value` is a regular expression, else `false`.
+     * @example
+     *
+     * _.isRegExp(/fred/);
+     * // => true
+     */
+    function isRegExp(value) {
+      return value && typeof value == 'object' && toString.call(value) == regexpClass || false;
+    }
+
+    /**
+     * Checks if `value` is a string.
+     *
+     * @static
+     * @memberOf _
+     * @category Objects
+     * @param {*} value The value to check.
+     * @returns {boolean} Returns `true` if the `value` is a string, else `false`.
+     * @example
+     *
+     * _.isString('fred');
+     * // => true
+     */
+    function isString(value) {
+      return typeof value == 'string' ||
+        value && typeof value == 'object' && toString.call(value) == stringClass || false;
+    }
+
+    /**
+     * Checks if `value` is `undefined`.
+     *
+     * @static
+     * @memberOf _
+     * @category Objects
+     * @param {*} value The value to check.
+     * @returns {boolean} Returns `true` if the `value` is `undefined`, else `false`.
+     * @example
+     *
+     * _.isUndefined(void 0);
+     * // => true
+     */
+    function isUndefined(value) {
+      return typeof value == 'undefined';
+    }
+
+    /**
+     * Creates an object with the same keys as `object` and values generated by
+     * running each own enumerable property of `object` through the callback.
+     * The callback is bound to `thisArg` and invoked with three arguments;
+     * (value, key, object).
+     *
+     * If a property name is provided for `callback` the created "_.pluck" style
+     * callback will return the property value of the given element.
+     *
+     * If an object is provided for `callback` the created "_.where" style callback
+     * will return `true` for elements that have the properties of the given object,
+     * else `false`.
+     *
+     * @static
+     * @memberOf _
+     * @category Objects
+     * @param {Object} object The object to iterate over.
+     * @param {Function|Object|string} [callback=identity] The function called
+     *  per iteration. If a property name or object is provided it will be used
+     *  to create a "_.pluck" or "_.where" style callback, respectively.
+     * @param {*} [thisArg] The `this` binding of `callback`.
+     * @returns {Array} Returns a new object with values of the results of each `callback` execution.
+     * @example
+     *
+     * _.mapValues({ 'a': 1, 'b': 2, 'c': 3} , function(num) { return num * 3; });
+     * // => { 'a': 3, 'b': 6, 'c': 9 }
+     *
+     * var characters = {
+     *   'fred': { 'name': 'fred', 'age': 40 },
+     *   'pebbles': { 'name': 'pebbles', 'age': 1 }
+     * };
+     *
+     * // using "_.pluck" callback shorthand
+     * _.mapValues(characters, 'age');
+     * // => { 'fred': 40, 'pebbles': 1 }
+     */
+    function mapValues(object, callback, thisArg) {
+      var result = {};
+      callback = lodash.createCallback(callback, thisArg, 3);
+
+      forOwn(object, function(value, key, object) {
+        result[key] = callback(value, key, object);
+      });
+      return result;
+    }
+
+    /**
+     * Recursively merges own enumerable properties of the source object(s), that
+     * don't resolve to `undefined` into the destination object. Subsequent sources
+     * will overwrite property assignments of previous sources. If a callback is
+     * provided it will be executed to produce the merged values of the destination
+     * and source properties. If the callback returns `undefined` merging will
+     * be handled by the method instead. The callback is bound to `thisArg` and
+     * invoked with two arguments; (objectValue, sourceValue).
+     *
+     * @static
+     * @memberOf _
+     * @category Objects
+     * @param {Object} object The destination object.
+     * @param {...Object} [source] The source objects.
+     * @param {Function} [callback] The function to customize merging properties.
+     * @param {*} [thisArg] The `this` binding of `callback`.
+     * @returns {Object} Returns the destination object.
+     * @example
+     *
+     * var names = {
+     *   'characters': [
+     *     { 'name': 'barney' },
+     *     { 'name': 'fred' }
+     *   ]
+     * };
+     *
+     * var ages = {
+     *   'characters': [
+     *     { 'age': 36 },
+     *     { 'age': 40 }
+     *   ]
+     * };
+     *
+     * _.merge(names, ages);
+     * // => { 'characters': [{ 'name': 'barney', 'age': 36 }, { 'name': 'fred', 'age': 40 }] }
+     *
+     * var food = {
+     *   'fruits': ['apple'],
+     *   'vegetables': ['beet']
+     * };
+     *
+     * var otherFood = {
+     *   'fruits': ['banana'],
+     *   'vegetables': ['carrot']
+     * };
+     *
+     * _.merge(food, otherFood, function(a, b) {
+     *   return _.isArray(a) ? a.concat(b) : undefined;
+     * });
+     * // => { 'fruits': ['apple', 'banana'], 'vegetables': ['beet', 'carrot] }
+     */
+    function merge(object) {
+      var args = arguments,
+          length = 2;
+
+      if (!isObject(object)) {
+        return object;
+      }
+      // allows working with `_.reduce` and `_.reduceRight` without using
+      // their `index` and `collection` arguments
+      if (typeof args[2] != 'number') {
+        length = args.length;
+      }
+      if (length > 3 && typeof args[length - 2] == 'function') {
+        var callback = baseCreateCallback(args[--length - 1], args[length--], 2);
+      } else if (length > 2 && typeof args[length - 1] == 'function') {
+        callback = args[--length];
+      }
+      var sources = slice(arguments, 1, length),
+          index = -1,
+          stackA = getArray(),
+          stackB = getArray();
+
+      while (++index < length) {
+        baseMerge(object, sources[index], callback, stackA, stackB);
+      }
+      releaseArray(stackA);
+      releaseArray(stackB);
+      return object;
+    }
+
+    /**
+     * Creates a shallow clone of `object` excluding the specified properties.
+     * Property names may be specified as individual arguments or as arrays of
+     * property names. If a callback is provided it will be executed for each
+     * property of `object` omitting the properties the callback returns truey
+     * for. The callback is bound to `thisArg` and invoked with three arguments;
+     * (value, key, object).
+     *
+     * @static
+     * @memberOf _
+     * @category Objects
+     * @param {Object} object The source object.
+     * @param {Function|...string|string[]} [callback] The properties to omit or the
+     *  function called per iteration.
+     * @param {*} [thisArg] The `this` binding of `callback`.
+     * @returns {Object} Returns an object without the omitted properties.
+     * @example
+     *
+     * _.omit({ 'name': 'fred', 'age': 40 }, 'age');
+     * // => { 'name': 'fred' }
+     *
+     * _.omit({ 'name': 'fred', 'age': 40 }, function(value) {
+     *   return typeof value == 'number';
+     * });
+     * // => { 'name': 'fred' }
+     */
+    function omit(object, callback, thisArg) {
+      var result = {};
+      if (typeof callback != 'function') {
+        var props = [];
+        forIn(object, function(value, key) {
+          props.push(key);
+        });
+        props = baseDifference(props, baseFlatten(arguments, true, false, 1));
+
+        var index = -1,
+            length = props.length;
+
+        while (++index < length) {
+          var key = props[index];
+          result[key] = object[key];
+        }
+      } else {
+        callback = lodash.createCallback(callback, thisArg, 3);
+        forIn(object, function(value, key, object) {
+          if (!callback(value, key, object)) {
+            result[key] = value;
+          }
+        });
+      }
+      return result;
+    }
+
+    /**
+     * Creates a two dimensional array of an object's key-value pairs,
+     * i.e. `[[key1, value1], [key2, value2]]`.
+     *
+     * @static
+     * @memberOf _
+     * @category Objects
+     * @param {Object} object The object to inspect.
+     * @returns {Array} Returns new array of key-value pairs.
+     * @example
+     *
+     * _.pairs({ 'barney': 36, 'fred': 40 });
+     * // => [['barney', 36], ['fred', 40]] (property order is not guaranteed across environments)
+     */
+    function pairs(object) {
+      var index = -1,
+          props = keys(object),
+          length = props.length,
+          result = Array(length);
+
+      while (++index < length) {
+        var key = props[index];
+        result[index] = [key, object[key]];
+      }
+      return result;
+    }
+
+    /**
+     * Creates a shallow clone of `object` composed of the specified properties.
+     * Property names may be specified as individual arguments or as arrays of
+     * property names. If a callback is provided it will be executed for each
+     * property of `object` picking the properties the callback returns truey
+     * for. The callback is bound to `thisArg` and invoked with three arguments;
+     * (value, key, object).
+     *
+     * @static
+     * @memberOf _
+     * @category Objects
+     * @param {Object} object The source object.
+     * @param {Function|...string|string[]} [callback] The function called per
+     *  iteration or property names to pick, specified as individual property
+     *  names or arrays of property names.
+     * @param {*} [thisArg] The `this` binding of `callback`.
+     * @returns {Object} Returns an object composed of the picked properties.
+     * @example
+     *
+     * _.pick({ 'name': 'fred', '_userid': 'fred1' }, 'name');
+     * // => { 'name': 'fred' }
+     *
+     * _.pick({ 'name': 'fred', '_userid': 'fred1' }, function(value, key) {
+     *   return key.charAt(0) != '_';
+     * });
+     * // => { 'name': 'fred' }
+     */
+    function pick(object, callback, thisArg) {
+      var result = {};
+      if (typeof callback != 'function') {
+        var index = -1,
+            props = baseFlatten(arguments, true, false, 1),
+            length = isObject(object) ? props.length : 0;
+
+        while (++index < length) {
+          var key = props[index];
+          if (key in object) {
+            result[key] = object[key];
+          }
+        }
+      } else {
+        callback = lodash.createCallback(callback, thisArg, 3);
+        forIn(object, function(value, key, object) {
+          if (callback(value, key, object)) {
+            result[key] = value;
+          }
+        });
+      }
+      return result;
+    }
+
+    /**
+     * An alternative to `_.reduce` this method transforms `object` to a new
+     * `accumulator` object which is the result of running each of its own
+     * enumerable properties through a callback, with each callback execution
+     * potentially mutating the `accumulator` object. The callback is bound to
+     * `thisArg` and invoked with four arguments; (accumulator, value, key, object).
+     * Callbacks may exit iteration early by explicitly returning `false`.
+     *
+     * @static
+     * @memberOf _
+     * @category Objects
+     * @param {Array|Object} object The object to iterate over.
+     * @param {Function} [callback=identity] The function called per iteration.
+     * @param {*} [accumulator] The custom accumulator value.
+     * @param {*} [thisArg] The `this` binding of `callback`.
+     * @returns {*} Returns the accumulated value.
+     * @example
+     *
+     * var squares = _.transform([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], function(result, num) {
+     *   num *= num;
+     *   if (num % 2) {
+     *     return result.push(num) < 3;
+     *   }
+     * });
+     * // => [1, 9, 25]
+     *
+     * var mapped = _.transform({ 'a': 1, 'b': 2, 'c': 3 }, function(result, num, key) {
+     *   result[key] = num * 3;
+     * });
+     * // => { 'a': 3, 'b': 6, 'c': 9 }
+     */
+    function transform(object, callback, accumulator, thisArg) {
+      var isArr = isArray(object);
+      if (accumulator == null) {
+        if (isArr) {
+          accumulator = [];
+        } else {
+          var ctor = object && object.constructor,
+              proto = ctor && ctor.prototype;
+
+          accumulator = baseCreate(proto);
+        }
+      }
+      if (callback) {
+        callback = lodash.createCallback(callback, thisArg, 4);
+        (isArr ? forEach : forOwn)(object, function(value, index, object) {
+          return callback(accumulator, value, index, object);
+        });
+      }
+      return accumulator;
+    }
+
+    /**
+     * Creates an array composed of the own enumerable property values of `object`.
+     *
+     * @static
+     * @memberOf _
+     * @category Objects
+     * @param {Object} object The object to inspect.
+     * @returns {Array} Returns an array of property values.
+     * @example
+     *
+     * _.values({ 'one': 1, 'two': 2, 'three': 3 });
+     * // => [1, 2, 3] (property order is not guaranteed across environments)
+     */
+    function values(object) {
+      var index = -1,
+          props = keys(object),
+          length = props.length,
+          result = Array(length);
+
+      while (++index < length) {
+        result[index] = object[props[index]];
+      }
+      return result;
+    }
+
+    /*--------------------------------------------------------------------------*/
+
+    /**
+     * Creates an array of elements from the specified indexes, or keys, of the
+     * `collection`. Indexes may be specified as individual arguments or as arrays
+     * of indexes.
+     *
+     * @static
+     * @memberOf _
+     * @category Collections
+     * @param {Array|Object|string} collection The collection to iterate over.
+     * @param {...(number|number[]|string|string[])} [index] The indexes of `collection`
+     *   to retrieve, specified as individual indexes or arrays of indexes.
+     * @returns {Array} Returns a new array of elements corresponding to the
+     *  provided indexes.
+     * @example
+     *
+     * _.at(['a', 'b', 'c', 'd', 'e'], [0, 2, 4]);
+     * // => ['a', 'c', 'e']
+     *
+     * _.at(['fred', 'barney', 'pebbles'], 0, 2);
+     * // => ['fred', 'pebbles']
+     */
+    function at(collection) {
+      var args = arguments,
+          index = -1,
+          props = baseFlatten(args, true, false, 1),
+          length = (args[2] && args[2][args[1]] === collection) ? 1 : props.length,
+          result = Array(length);
+
+      while(++index < length) {
+        result[index] = collection[props[index]];
+      }
+      return result;
+    }
+
+    /**
+     * Checks if a given value is present in a collection using strict equality
+     * for comparisons, i.e. `===`. If `fromIndex` is negative, it is used as the
+     * offset from the end of the collection.
+     *
+     * @static
+     * @memberOf _
+     * @alias include
+     * @category Collections
+     * @param {Array|Object|string} collection The collection to iterate over.
+     * @param {*} target The value to check for.
+     * @param {number} [fromIndex=0] The index to search from.
+     * @returns {boolean} Returns `true` if the `target` element is found, else `false`.
+     * @example
+     *
+     * _.contains([1, 2, 3], 1);
+     * // => true
+     *
+     * _.contains([1, 2, 3], 1, 2);
+     * // => false
+     *
+     * _.contains({ 'name': 'fred', 'age': 40 }, 'fred');
+     * // => true
+     *
+     * _.contains('pebbles', 'eb');
+     * // => true
+     */
+    function contains(collection, target, fromIndex) {
+      var index = -1,
+          indexOf = getIndexOf(),
+          length = collection ? collection.length : 0,
+          result = false;
+
+      fromIndex = (fromIndex < 0 ? nativeMax(0, length + fromIndex) : fromIndex) || 0;
+      if (isArray(collection)) {
+        result = indexOf(collection, target, fromIndex) > -1;
+      } else if (typeof length == 'number') {
+        result = (isString(collection) ? collection.indexOf(target, fromIndex) : indexOf(collection, target, fromIndex)) > -1;
+      } else {
+        forOwn(collection, function(value) {
+          if (++index >= fromIndex) {
+            return !(result = value === target);
+          }
+        });
+      }
+      return result;
+    }
+
+    /**
+     * Creates an object composed of keys generated from the results of running
+     * each element of `collection` through the callback. The corresponding value
+     * of each key is the number of times the key was returned by the callback.
+     * The callback is bound to `thisArg` and invoked with three arguments;
+     * (value, index|key, collection).
+     *
+     * If a property name is provided for `callback` the created "_.pluck" style
+     * callback will return the property value of the given element.
+     *
+     * If an object is provided for `callback` the created "_.where" style callback
+     * will return `true` for elements that have the properties of the given object,
+     * else `false`.
+     *
+     * @static
+     * @memberOf _
+     * @category Collections
+     * @param {Array|Object|string} collection The collection to iterate over.
+     * @param {Function|Object|string} [callback=identity] The function called
+     *  per iteration. If a property name or object is provided it will be used
+     *  to create a "_.pluck" or "_.where" style callback, respectively.
+     * @param {*} [thisArg] The `this` binding of `callback`.
+     * @returns {Object} Returns the composed aggregate object.
+     * @example
+     *
+     * _.countBy([4.3, 6.1, 6.4], function(num) { return Math.floor(num); });
+     * // => { '4': 1, '6': 2 }
+     *
+     * _.countBy([4.3, 6.1, 6.4], function(num) { return this.floor(num); }, Math);
+     * // => { '4': 1, '6': 2 }
+     *
+     * _.countBy(['one', 'two', 'three'], 'length');
+     * // => { '3': 2, '5': 1 }
+     */
+    var countBy = createAggregator(function(result, value, key) {
+      (hasOwnProperty.call(result, key) ? result[key]++ : result[key] = 1);
+    });
+
+    /**
+     * Checks if the given callback returns truey value for **all** elements of
+     * a collection. The callback is bound to `thisArg` and invoked with three
+     * arguments; (value, index|key, collection).
+     *
+     * If a property name is provided for `callback` the created "_.pluck" style
+     * callback will return the property value of the given element.
+     *
+     * If an object is provided for `callback` the created "_.where" style callback
+     * will return `true` for elements that have the properties of the given object,
+     * else `false`.
+     *
+     * @static
+     * @memberOf _
+     * @alias all
+     * @category Collections
+     * @param {Array|Object|string} collection The collection to iterate over.
+     * @param {Function|Object|string} [callback=identity] The function called
+     *  per iteration. If a property name or object is provided it will be used
+     *  to create a "_.pluck" or "_.where" style callback, respectively.
+     * @param {*} [thisArg] The `this` binding of `callback`.
+     * @returns {boolean} Returns `true` if all elements passed the callback check,
+     *  else `false`.
+     * @example
+     *
+     * _.every([true, 1, null, 'yes']);
+     * // => false
+     *
+     * var characters = [
+     *   { 'name': 'barney', 'age': 36 },
+     *   { 'name': 'fred',   'age': 40 }
+     * ];
+     *
+     * // using "_.pluck" callback shorthand
+     * _.every(characters, 'age');
+     * // => true
+     *
+     * // using "_.where" callback shorthand
+     * _.every(characters, { 'age': 36 });
+     * // => false
+     */
+    function every(collection, callback, thisArg) {
+      var result = true;
+      callback = lodash.createCallback(callback, thisArg, 3);
+
+      var index = -1,
+          length = collection ? collection.length : 0;
+
+      if (typeof length == 'number') {
+        while (++index < length) {
+          if (!(result = !!callback(collection[index], index, collection))) {
+            break;
+          }
+        }
+      } else {
+        forOwn(collection, function(value, index, collection) {
+          return (result = !!callback(value, index, collection));
+        });
+      }
+      return result;
+    }
+
+    /**
+     * Iterates over elements of a collection, returning an array of all elements
+     * the callback returns truey for. The callback is bound to `thisArg` and
+     * invoked with three arguments; (value, index|key, collection).
+     *
+     * If a property name is provided for `callback` the created "_.pluck" style
+     * callback will return the property value of the given element.
+     *
+     * If an object is provided for `callback` the created "_.where" style callback
+     * will return `true` for elements that have the properties of the given object,
+     * else `false`.
+     *
+     * @static
+     * @memberOf _
+     * @alias select
+     * @category Collections
+     * @param {Array|Object|string} collection The collection to iterate over.
+     * @param {Function|Object|string} [callback=identity] The function called
+     *  per iteration. If a property name or object is provided it will be used
+     *  to create a "_.pluck" or "_.where" style callback, respectively.
+     * @param {*} [thisArg] The `this` binding of `callback`.
+     * @returns {Array} Returns a new array of elements that passed the callback check.
+     * @example
+     *
+     * var evens = _.filter([1, 2, 3, 4, 5, 6], function(num) { return num % 2 == 0; });
+     * // => [2, 4, 6]
+     *
+     * var characters = [
+     *   { 'name': 'barney', 'age': 36, 'blocked': false },
+     *   { 'name': 'fred',   'age': 40, 'blocked': true }
+     * ];
+     *
+     * // using "_.pluck" callback shorthand
+     * _.filter(characters, 'blocked');
+     * // => [{ 'name': 'fred', 'age': 40, 'blocked': true }]
+     *
+     * // using "_.where" callback shorthand
+     * _.filter(characters, { 'age': 36 });
+     * // => [{ 'name': 'barney', 'age': 36, 'blocked': false }]
+     */
+    function filter(collection, callback, thisArg) {
+      var result = [];
+      callback = lodash.createCallback(callback, thisArg, 3);
+
+      var index = -1,
+          length = collection ? collection.length : 0;
+
+      if (typeof length == 'number') {
+        while (++index < length) {
+          var value = collection[index];
+          if (callback(value, index, collection)) {
+            result.push(value);
+          }
+        }
+      } else {
+        forOwn(collection, function(value, index, collection) {
+          if (callback(value, index, collection)) {
+            result.push(value);
+          }
+        });
+      }
+      return result;
+    }
+
+    /**
+     * Iterates over elements of a collection, returning the first element that
+     * the callback returns truey for. The callback is bound to `thisArg` and
+     * invoked with three arguments; (value, index|key, collection).
+     *
+     * If a property name is provided for `callback` the created "_.pluck" style
+     * callback will return the property value of the given element.
+     *
+     * If an object is provided for `callback` the created "_.where" style callback
+     * will return `true` for elements that have the properties of the given object,
+     * else `false`.
+     *
+     * @static
+     * @memberOf _
+     * @alias detect, findWhere
+     * @category Collections
+     * @param {Array|Object|string} collection The collection to iterate over.
+     * @param {Function|Object|string} [callback=identity] The function called
+     *  per iteration. If a property name or object is provided it will be used
+     *  to create a "_.pluck" or "_.where" style callback, respectively.
+     * @param {*} [thisArg] The `this` binding of `callback`.
+     * @returns {*} Returns the found element, else `undefined`.
+     * @example
+     *
+     * var characters = [
+     *   { 'name': 'barney',  'age': 36, 'blocked': false },
+     *   { 'name': 'fred',    'age': 40, 'blocked': true },
+     *   { 'name': 'pebbles', 'age': 1,  'blocked': false }
+     * ];
+     *
+     * _.find(characters, function(chr) {
+     *   return chr.age < 40;
+     * });
+     * // => { 'name': 'barney', 'age': 36, 'blocked': false }
+     *
+     * // using "_.where" callback shorthand
+     * _.find(characters, { 'age': 1 });
+     * // =>  { 'name': 'pebbles', 'age': 1, 'blocked': false }
+     *
+     * // using "_.pluck" callback shorthand
+     * _.find(characters, 'blocked');
+     * // => { 'name': 'fred', 'age': 40, 'blocked': true }
+     */
+    function find(collection, callback, thisArg) {
+      callback = lodash.createCallback(callback, thisArg, 3);
+
+      var index = -1,
+          length = collection ? collection.length : 0;
+
+      if (typeof length == 'number') {
+        while (++index < length) {
+          var value = collection[index];
+          if (callback(value, index, collection)) {
+            return value;
+          }
+        }
+      } else {
+        var result;
+        forOwn(collection, function(value, index, collection) {
+          if (callback(value, index, collection)) {
+            result = value;
+            return false;
+          }
+        });
+        return result;
+      }
+    }
+
+    /**
+     * This method is like `_.find` except that it iterates over elements
+     * of a `collection` from right to left.
+     *
+     * @static
+     * @memberOf _
+     * @category Collections
+     * @param {Array|Object|string} collection The collection to iterate over.
+     * @param {Function|Object|string} [callback=identity] The function called
+     *  per iteration. If a property name or object is provided it will be used
+     *  to create a "_.pluck" or "_.where" style callback, respectively.
+     * @param {*} [thisArg] The `this` binding of `callback`.
+     * @returns {*} Returns the found element, else `undefined`.
+     * @example
+     *
+     * _.findLast([1, 2, 3, 4], function(num) {
+     *   return num % 2 == 1;
+     * });
+     * // => 3
+     */
+    function findLast(collection, callback, thisArg) {
+      var result;
+      callback = lodash.createCallback(callback, thisArg, 3);
+      forEachRight(collection, function(value, index, collection) {
+        if (callback(value, index, collection)) {
+          result = value;
+          return false;
+        }
+      });
+      return result;
+    }
+
+    /**
+     * Iterates over elements of a collection, executing the callback for each
+     * element. The callback is bound to `thisArg` and invoked with three arguments;
+     * (value, index|key, collection). Callbacks may exit iteration early by
+     * explicitly returning `false`.
+     *
+     * Note: As with other "Collections" methods, objects with a `length` property
+     * are iterated like arrays. To avoid this behavior `_.forIn` or `_.forOwn`
+     * may be used for object iteration.
+     *
+     * @static
+     * @memberOf _
+     * @alias each
+     * @category Collections
+     * @param {Array|Object|string} collection The collection to iterate over.
+     * @param {Function} [callback=identity] The function called per iteration.
+     * @param {*} [thisArg] The `this` binding of `callback`.
+     * @returns {Array|Object|string} Returns `collection`.
+     * @example
+     *
+     * _([1, 2, 3]).forEach(function(num) { console.log(num); }).join(',');
+     * // => logs each number and returns '1,2,3'
+     *
+     * _.forEach({ 'one': 1, 'two': 2, 'three': 3 }, function(num) { console.log(num); });
+     * // => logs each number and returns the object (property order is not guaranteed across environments)
+     */
+    function forEach(collection, callback, thisArg) {
+      var index = -1,
+          length = collection ? collection.length : 0;
+
+      callback = callback && typeof thisArg == 'undefined' ? callback : baseCreateCallback(callback, thisArg, 3);
+      if (typeof length == 'number') {
+        while (++index < length) {
+          if (callback(collection[index], index, collection) === false) {
+            break;
+          }
+        }
+      } else {
+        forOwn(collection, callback);
+      }
+      return collection;
+    }
+
+    /**
+     * This method is like `_.forEach` except that it iterates over elements
+     * of a `collection` from right to left.
+     *
+     * @static
+     * @memberOf _
+     * @alias eachRight
+     * @category Collections
+     * @param {Array|Object|string} collection The collection to iterate over.
+     * @param {Function} [callback=identity] The function called per iteration.
+     * @param {*} [thisArg] The `this` binding of `callback`.
+     * @returns {Array|Object|string} Returns `collection`.
+     * @example
+     *
+     * _([1, 2, 3]).forEachRight(function(num) { console.log(num); }).join(',');
+     * // => logs each number from right to left and returns '3,2,1'
+     */
+    function forEachRight(collection, callback, thisArg) {
+      var length = collection ? collection.length : 0;
+      callback = callback && typeof thisArg == 'undefined' ? callback : baseCreateCallback(callback, thisArg, 3);
+      if (typeof length == 'number') {
+        while (length--) {
+          if (callback(collection[length], length, collection) === false) {
+            break;
+          }
+        }
+      } else {
+        var props = keys(collection);
+        length = props.length;
+        forOwn(collection, function(value, key, collection) {
+          key = props ? props[--length] : --length;
+          return callback(collection[key], key, collection);
+        });
+      }
+      return collection;
+    }
+
+    /**
+     * Creates an object composed of keys generated from the results of running
+     * each element of a collection through the callback. The corresponding value
+     * of each key is an array of the elements responsible for generating the key.
+     * The callback is bound to `thisArg` and invoked with three arguments;
+     * (value, index|key, collection).
+     *
+     * If a property name is provided for `callback` the created "_.pluck" style
+     * callback will return the property value of the given element.
+     *
+     * If an object is provided for `callback` the created "_.where" style callback
+     * will return `true` for elements that have the properties of the given object,
+     * else `false`
+     *
+     * @static
+     * @memberOf _
+     * @category Collections
+     * @param {Array|Object|string} collection The collection to iterate over.
+     * @param {Function|Object|string} [callback=identity] The function called
+     *  per iteration. If a property name or object is provided it will be used
+     *  to create a "_.pluck" or "_.where" style callback, respectively.
+     * @param {*} [thisArg] The `this` binding of `callback`.
+     * @returns {Object} Returns the composed aggregate object.
+     * @example
+     *
+     * _.groupBy([4.2, 6.1, 6.4], function(num) { return Math.floor(num); });
+     * // => { '4': [4.2], '6': [6.1, 6.4] }
+     *
+     * _.groupBy([4.2, 6.1, 6.4], function(num) { return this.floor(num); }, Math);
+     * // => { '4': [4.2], '6': [6.1, 6.4] }
+     *
+     * // using "_.pluck" callback shorthand
+     * _.groupBy(['one', 'two', 'three'], 'length');
+     * // => { '3': ['one', 'two'], '5': ['three'] }
+     */
+    var groupBy = createAggregator(function(result, value, key) {
+      (hasOwnProperty.call(result, key) ? result[key] : result[key] = []).push(value);
+    });
+
+    /**
+     * Creates an object composed of keys generated from the results of running
+     * each element of the collection through the given callback. The corresponding
+     * value of each key is the last element responsible for generating the key.
+     * The callback is bound to `thisArg` and invoked with three arguments;
+     * (value, index|key, collection).
+     *
+     * If a property name is provided for `callback` the created "_.pluck" style
+     * callback will return the property value of the given element.
+     *
+     * If an object is provided for `callback` the created "_.where" style callback
+     * will return `true` for elements that have the properties of the given object,
+     * else `false`.
+     *
+     * @static
+     * @memberOf _
+     * @category Collections
+     * @param {Array|Object|string} collection The collection to iterate over.
+     * @param {Function|Object|string} [callback=identity] The function called
+     *  per iteration. If a property name or object is provided it will be used
+     *  to create a "_.pluck" or "_.where" style callback, respectively.
+     * @param {*} [thisArg] The `this` binding of `callback`.
+     * @returns {Object} Returns the composed aggregate object.
+     * @example
+     *
+     * var keys = [
+     *   { 'dir': 'left', 'code': 97 },
+     *   { 'dir': 'right', 'code': 100 }
+     * ];
+     *
+     * _.indexBy(keys, 'dir');
+     * // => { 'left': { 'dir': 'left', 'code': 97 }, 'right': { 'dir': 'right', 'code': 100 } }
+     *
+     * _.indexBy(keys, function(key) { return String.fromCharCode(key.code); });
+     * // => { 'a': { 'dir': 'left', 'code': 97 }, 'd': { 'dir': 'right', 'code': 100 } }
+     *
+     * _.indexBy(characters, function(key) { this.fromCharCode(key.code); }, String);
+     * // => { 'a': { 'dir': 'left', 'code': 97 }, 'd': { 'dir': 'right', 'code': 100 } }
+     */
+    var indexBy = createAggregator(function(result, value, key) {
+      result[key] = value;
+    });
+
+    /**
+     * Invokes the method named by `methodName` on each element in the `collection`
+     * returning an array of the results of each invoked method. Additional arguments
+     * will be provided to each invoked method. If `methodName` is a function it
+     * will be invoked for, and `this` bound to, each element in the `collection`.
+     *
+     * @static
+     * @memberOf _
+     * @category Collections
+     * @param {Array|Object|string} collection The collection to iterate over.
+     * @param {Function|string} methodName The name of the method to invoke or
+     *  the function invoked per iteration.
+     * @param {...*} [arg] Arguments to invoke the method with.
+     * @returns {Array} Returns a new array of the results of each invoked method.
+     * @example
+     *
+     * _.invoke([[5, 1, 7], [3, 2, 1]], 'sort');
+     * // => [[1, 5, 7], [1, 2, 3]]
+     *
+     * _.invoke([123, 456], String.prototype.split, '');
+     * // => [['1', '2', '3'], ['4', '5', '6']]
+     */
+    function invoke(collection, methodName) {
+      var args = slice(arguments, 2),
+          index = -1,
+          isFunc = typeof methodName == 'function',
+          length = collection ? collection.length : 0,
+          result = Array(typeof length == 'number' ? length : 0);
+
+      forEach(collection, function(value) {
+        result[++index] = (isFunc ? methodName : value[methodName]).apply(value, args);
+      });
+      return result;
+    }
+
+    /**
+     * Creates an array of values by running each element in the collection
+     * through the callback. The callback is bound to `thisArg` and invoked with
+     * three arguments; (value, index|key, collection).
+     *
+     * If a property name is provided for `callback` the created "_.pluck" style
+     * callback will return the property value of the given element.
+     *
+     * If an object is provided for `callback` the created "_.where" style callback
+     * will return `true` for elements that have the properties of the given object,
+     * else `false`.
+     *
+     * @static
+     * @memberOf _
+     * @alias collect
+     * @category Collections
+     * @param {Array|Object|string} collection The collection to iterate over.
+     * @param {Function|Object|string} [callback=identity] The function called
+     *  per iteration. If a property name or object is provided it will be used
+     *  to create a "_.pluck" or "_.where" style callback, respectively.
+     * @param {*} [thisArg] The `this` binding of `callback`.
+     * @returns {Array} Returns a new array of the results of each `callback` execution.
+     * @example
+     *
+     * _.map([1, 2, 3], function(num) { return num * 3; });
+     * // => [3, 6, 9]
+     *
+     * _.map({ 'one': 1, 'two': 2, 'three': 3 }, function(num) { return num * 3; });
+     * // => [3, 6, 9] (property order is not guaranteed across environments)
+     *
+     * var characters = [
+     *   { 'name': 'barney', 'age': 36 },
+     *   { 'name': 'fred',   'age': 40 }
+     * ];
+     *
+     * // using "_.pluck" callback shorthand
+     * _.map(characters, 'name');
+     * // => ['barney', 'fred']
+     */
+    function map(collection, callback, thisArg) {
+      var index = -1,
+          length = collection ? collection.length : 0;
+
+      callback = lodash.createCallback(callback, thisArg, 3);
+      if (typeof length == 'number') {
+        var result = Array(length);
+        while (++index < length) {
+          result[index] = callback(collection[index], index, collection);
+        }
+      } else {
+        result = [];
+        forOwn(collection, function(value, key, collection) {
+          result[++index] = callback(value, key, collection);
+        });
+      }
+      return result;
+    }
+
+    /**
+     * Retrieves the maximum value of a collection. If the collection is empty or
+     * falsey `-Infinity` is returned. If a callback is provided it will be executed
+     * for each value in the collection to generate the criterion by which the value
+     * is ranked. The callback is bound to `thisArg` and invoked with three
+     * arguments; (value, index, collection).
+     *
+     * If a property name is provided for `callback` the created "_.pluck" style
+     * callback will return the property value of the given element.
+     *
+     * If an object is provided for `callback` the created "_.where" style callback
+     * will return `true` for elements that have the properties of the given object,
+     * else `false`.
+     *
+     * @static
+     * @memberOf _
+     * @category Collections
+     * @param {Array|Object|string} collection The collection to iterate over.
+     * @param {Function|Object|string} [callback=identity] The function called
+     *  per iteration. If a property name or object is provided it will be used
+     *  to create a "_.pluck" or "_.where" style callback, respectively.
+     * @param {*} [thisArg] The `this` binding of `callback`.
+     * @returns {*} Returns the maximum value.
+     * @example
+     *
+     * _.max([4, 2, 8, 6]);
+     * // => 8
+     *
+     * var characters = [
+     *   { 'name': 'barney', 'age': 36 },
+     *   { 'name': 'fred',   'age': 40 }
+     * ];
+     *
+     * _.max(characters, function(chr) { return chr.age; });
+     * // => { 'name': 'fred', 'age': 40 };
+     *
+     * // using "_.pluck" callback shorthand
+     * _.max(characters, 'age');
+     * // => { 'name': 'fred', 'age': 40 };
+     */
+    function max(collection, callback, thisArg) {
+      var computed = -Infinity,
+          result = computed;
+
+      // allows working with functions like `_.map` without using
+      // their `index` argument as a callback
+      if (typeof callback != 'function' && thisArg && thisArg[callback] === collection) {
+        callback = null;
+      }
+      if (callback == null && isArray(collection)) {
+        var index = -1,
+            length = collection.length;
+
+        while (++index < length) {
+          var value = collection[index];
+          if (value > result) {
+            result = value;
+          }
+        }
+      } else {
+        callback = (callback == null && isString(collection))
+          ? charAtCallback
+          : lodash.createCallback(callback, thisArg, 3);
+
+        forEach(collection, function(value, index, collection) {
+          var current = callback(value, index, collection);
+          if (current > computed) {
+            computed = current;
+            result = value;
+          }
+        });
+      }
+      return result;
+    }
+
+    /**
+     * Retrieves the minimum value of a collection. If the collection is empty or
+     * falsey `Infinity` is returned. If a callback is provided it will be executed
+     * for each value in the collection to generate the criterion by which the value
+     * is ranked. The callback is bound to `thisArg` and invoked with three
+     * arguments; (value, index, collection).
+     *
+     * If a property name is provided for `callback` the created "_.pluck" style
+     * callback will return the property value of the given element.
+     *
+     * If an object is provided for `callback` the created "_.where" style callback
+     * will return `true` for elements that have the properties of the given object,
+     * else `false`.
+     *
+     * @static
+     * @memberOf _
+     * @category Collections
+     * @param {Array|Object|string} collection The collection to iterate over.
+     * @param {Function|Object|string} [callback=identity] The function called
+     *  per iteration. If a property name or object is provided it will be used
+     *  to create a "_.pluck" or "_.where" style callback, respectively.
+     * @param {*} [thisArg] The `this` binding of `callback`.
+     * @returns {*} Returns the minimum value.
+     * @example
+     *
+     * _.min([4, 2, 8, 6]);
+     * // => 2
+     *
+     * var characters = [
+     *   { 'name': 'barney', 'age': 36 },
+     *   { 'name': 'fred',   'age': 40 }
+     * ];
+     *
+     * _.min(characters, function(chr) { return chr.age; });
+     * // => { 'name': 'barney', 'age': 36 };
+     *
+     * // using "_.pluck" callback shorthand
+     * _.min(characters, 'age');
+     * // => { 'name': 'barney', 'age': 36 };
+     */
+    function min(collection, callback, thisArg) {
+      var computed = Infinity,
+          result = computed;
+
+      // allows working with functions like `_.map` without using
+      // their `index` argument as a callback
+      if (typeof callback != 'function' && thisArg && thisArg[callback] === collection) {
+        callback = null;
+      }
+      if (callback == null && isArray(collection)) {
+        var index = -1,
+            length = collection.length;
+
+        while (++index < length) {
+          var value = collection[index];
+          if (value < result) {
+            result = value;
+          }
+        }
+      } else {
+        callback = (callback == null && isString(collection))
+          ? charAtCallback
+          : lodash.createCallback(callback, thisArg, 3);
+
+        forEach(collection, function(value, index, collection) {
+          var current = callback(value, index, collection);
+          if (current < computed) {
+            computed = current;
+            result = value;
+          }
+        });
+      }
+      return result;
+    }
+
+    /**
+     * Retrieves the value of a specified property from all elements in the collection.
+     *
+     * @static
+     * @memberOf _
+     * @type Function
+     * @category Collections
+     * @param {Array|Object|string} collection The collection to iterate over.
+     * @param {string} property The name of the property to pluck.
+     * @returns {Array} Returns a new array of property values.
+     * @example
+     *
+     * var characters = [
+     *   { 'name': 'barney', 'age': 36 },
+     *   { 'name': 'fred',   'age': 40 }
+     * ];
+     *
+     * _.pluck(characters, 'name');
+     * // => ['barney', 'fred']
+     */
+    var pluck = map;
+
+    /**
+     * Reduces a collection to a value which is the accumulated result of running
+     * each element in the collection through the callback, where each successive
+     * callback execution consumes the return value of the previous execution. If
+     * `accumulator` is not provided the first element of the collection will be
+     * used as the initial `accumulator` value. The callback is bound to `thisArg`
+     * and invoked with four arguments; (accumulator, value, index|key, collection).
+     *
+     * @static
+     * @memberOf _
+     * @alias foldl, inject
+     * @category Collections
+     * @param {Array|Object|string} collection The collection to iterate over.
+     * @param {Function} [callback=identity] The function called per iteration.
+     * @param {*} [accumulator] Initial value of the accumulator.
+     * @param {*} [thisArg] The `this` binding of `callback`.
+     * @returns {*} Returns the accumulated value.
+     * @example
+     *
+     * var sum = _.reduce([1, 2, 3], function(sum, num) {
+     *   return sum + num;
+     * });
+     * // => 6
+     *
+     * var mapped = _.reduce({ 'a': 1, 'b': 2, 'c': 3 }, function(result, num, key) {
+     *   result[key] = num * 3;
+     *   return result;
+     * }, {});
+     * // => { 'a': 3, 'b': 6, 'c': 9 }
+     */
+    function reduce(collection, callback, accumulator, thisArg) {
+      if (!collection) return accumulator;
+      var noaccum = arguments.length < 3;
+      callback = lodash.createCallback(callback, thisArg, 4);
+
+      var index = -1,
+          length = collection.length;
+
+      if (typeof length == 'number') {
+        if (noaccum) {
+          accumulator = collection[++index];
+        }
+        while (++index < length) {
+          accumulator = callback(accumulator, collection[index], index, collection);
+        }
+      } else {
+        forOwn(collection, function(value, index, collection) {
+          accumulator = noaccum
+            ? (noaccum = false, value)
+            : callback(accumulator, value, index, collection)
+        });
+      }
+      return accumulator;
+    }
+
+    /**
+     * This method is like `_.reduce` except that it iterates over elements
+     * of a `collection` from right to left.
+     *
+     * @static
+     * @memberOf _
+     * @alias foldr
+     * @category Collections
+     * @param {Array|Object|string} collection The collection to iterate over.
+     * @param {Function} [callback=identity] The function called per iteration.
+     * @param {*} [accumulator] Initial value of the accumulator.
+     * @param {*} [thisArg] The `this` binding of `callback`.
+     * @returns {*} Returns the accumulated value.
+     * @example
+     *
+     * var list = [[0, 1], [2, 3], [4, 5]];
+     * var flat = _.reduceRight(list, function(a, b) { return a.concat(b); }, []);
+     * // => [4, 5, 2, 3, 0, 1]
+     */
+    function reduceRight(collection, callback, accumulator, thisArg) {
+      var noaccum = arguments.length < 3;
+      callback = lodash.createCallback(callback, thisArg, 4);
+      forEachRight(collection, function(value, index, collection) {
+        accumulator = noaccum
+          ? (noaccum = false, value)
+          : callback(accumulator, value, index, collection);
+      });
+      return accumulator;
+    }
+
+    /**
+     * The opposite of `_.filter` this method returns the elements of a
+     * collection that the callback does **not** return truey for.
+     *
+     * If a property name is provided for `callback` the created "_.pluck" style
+     * callback will return the property value of the given element.
+     *
+     * If an object is provided for `callback` the created "_.where" style callback
+     * will return `true` for elements that have the properties of the given object,
+     * else `false`.
+     *
+     * @static
+     * @memberOf _
+     * @category Collections
+     * @param {Array|Object|string} collection The collection to iterate over.
+     * @param {Function|Object|string} [callback=identity] The function called
+     *  per iteration. If a property name or object is provided it will be used
+     *  to create a "_.pluck" or "_.where" style callback, respectively.
+     * @param {*} [thisArg] The `this` binding of `callback`.
+     * @returns {Array} Returns a new array of elements that failed the callback check.
+     * @example
+     *
+     * var odds = _.reject([1, 2, 3, 4, 5, 6], function(num) { return num % 2 == 0; });
+     * // => [1, 3, 5]
+     *
+     * var characters = [
+     *   { 'name': 'barney', 'age': 36, 'blocked': false },
+     *   { 'name': 'fred',   'age': 40, 'blocked': true }
+     * ];
+     *
+     * // using "_.pluck" callback shorthand
+     * _.reject(characters, 'blocked');
+     * // => [{ 'name': 'barney', 'age': 36, 'blocked': false }]
+     *
+     * // using "_.where" callback shorthand
+     * _.reject(characters, { 'age': 36 });
+     * // => [{ 'name': 'fred', 'age': 40, 'blocked': true }]
+     */
+    function reject(collection, callback, thisArg) {
+      callback = lodash.createCallback(callback, thisArg, 3);
+      return filter(collection, function(value, index, collection) {
+        return !callback(value, index, collection);
+      });
+    }
+
+    /**
+     * Retrieves a random element or `n` random elements from a collection.
+     *
+     * @static
+     * @memberOf _
+     * @category Collections
+     * @param {Array|Object|string} collection The collection to sample.
+     * @param {number} [n] The number of elements to sample.
+     * @param- {Object} [guard] Allows working with functions like `_.map`
+     *  without using their `index` arguments as `n`.
+     * @returns {Array} Returns the random sample(s) of `collection`.
+     * @example
+     *
+     * _.sample([1, 2, 3, 4]);
+     * // => 2
+     *
+     * _.sample([1, 2, 3, 4], 2);
+     * // => [3, 1]
+     */
+    function sample(collection, n, guard) {
+      if (collection && typeof collection.length != 'number') {
+        collection = values(collection);
+      }
+      if (n == null || guard) {
+        return collection ? collection[baseRandom(0, collection.length - 1)] : undefined;
+      }
+      var result = shuffle(collection);
+      result.length = nativeMin(nativeMax(0, n), result.length);
+      return result;
+    }
+
+    /**
+     * Creates an array of shuffled values, using a version of the Fisher-Yates
+     * shuffle. See http://en.wikipedia.org/wiki/Fisher-Yates_shuffle.
+     *
+     * @static
+     * @memberOf _
+     * @category Collections
+     * @param {Array|Object|string} collection The collection to shuffle.
+     * @returns {Array} Returns a new shuffled collection.
+     * @example
+     *
+     * _.shuffle([1, 2, 3, 4, 5, 6]);
+     * // => [4, 1, 6, 3, 5, 2]
+     */
+    function shuffle(collection) {
+      var index = -1,
+          length = collection ? collection.length : 0,
+          result = Array(typeof length == 'number' ? length : 0);
+
+      forEach(collection, function(value) {
+        var rand = baseRandom(0, ++index);
+        result[index] = result[rand];
+        result[rand] = value;
+      });
+      return result;
+    }
+
+    /**
+     * Gets the size of the `collection` by returning `collection.length` for arrays
+     * and array-like objects or the number of own enumerable properties for objects.
+     *
+     * @static
+     * @memberOf _
+     * @category Collections
+     * @param {Array|Object|string} collection The collection to inspect.
+     * @returns {number} Returns `collection.length` or number of own enumerable properties.
+     * @example
+     *
+     * _.size([1, 2]);
+     * // => 2
+     *
+     * _.size({ 'one': 1, 'two': 2, 'three': 3 });
+     * // => 3
+     *
+     * _.size('pebbles');
+     * // => 7
+     */
+    function size(collection) {
+      var length = collection ? collection.length : 0;
+      return typeof length == 'number' ? length : keys(collection).length;
+    }
+
+    /**
+     * Checks if the callback returns a truey value for **any** element of a
+     * collection. The function returns as soon as it finds a passing value and
+     * does not iterate over the entire collection. The callback is bound to
+     * `thisArg` and invoked with three arguments; (value, index|key, collection).
+     *
+     * If a property name is provided for `callback` the created "_.pluck" style
+     * callback will return the property value of the given element.
+     *
+     * If an object is provided for `callback` the created "_.where" style callback
+     * will return `true` for elements that have the properties of the given object,
+     * else `false`.
+     *
+     * @static
+     * @memberOf _
+     * @alias any
+     * @category Collections
+     * @param {Array|Object|string} collection The collection to iterate over.
+     * @param {Function|Object|string} [callback=identity] The function called
+     *  per iteration. If a property name or object is provided it will be used
+     *  to create a "_.pluck" or "_.where" style callback, respectively.
+     * @param {*} [thisArg] The `this` binding of `callback`.
+     * @returns {boolean} Returns `true` if any element passed the callback check,
+     *  else `false`.
+     * @example
+     *
+     * _.some([null, 0, 'yes', false], Boolean);
+     * // => true
+     *
+     * var characters = [
+     *   { 'name': 'barney', 'age': 36, 'blocked': false },
+     *   { 'name': 'fred',   'age': 40, 'blocked': true }
+     * ];
+     *
+     * // using "_.pluck" callback shorthand
+     * _.some(characters, 'blocked');
+     * // => true
+     *
+     * // using "_.where" callback shorthand
+     * _.some(characters, { 'age': 1 });
+     * // => false
+     */
+    function some(collection, callback, thisArg) {
+      var result;
+      callback = lodash.createCallback(callback, thisArg, 3);
+
+      var index = -1,
+          length = collection ? collection.length : 0;
+
+      if (typeof length == 'number') {
+        while (++index < length) {
+          if ((result = callback(collection[index], index, collection))) {
+            break;
+          }
+        }
+      } else {
+        forOwn(collection, function(value, index, collection) {
+          return !(result = callback(value, index, collection));
+        });
+      }
+      return !!result;
+    }
+
+    /**
+     * Creates an array of elements, sorted in ascending order by the results of
+     * running each element in a collection through the callback. This method
+     * performs a stable sort, that is, it will preserve the original sort order
+     * of equal elements. The callback is bound to `thisArg` and invoked with
+     * three arguments; (value, index|key, collection).
+     *
+     * If a property name is provided for `callback` the created "_.pluck" style
+     * callback will return the property value of the given element.
+     *
+     * If an array of property names is provided for `callback` the collection
+     * will be sorted by each property value.
+     *
+     * If an object is provided for `callback` the created "_.where" style callback
+     * will return `true` for elements that have the properties of the given object,
+     * else `false`.
+     *
+     * @static
+     * @memberOf _
+     * @category Collections
+     * @param {Array|Object|string} collection The collection to iterate over.
+     * @param {Array|Function|Object|string} [callback=identity] The function called
+     *  per iteration. If a property name or object is provided it will be used
+     *  to create a "_.pluck" or "_.where" style callback, respectively.
+     * @param {*} [thisArg] The `this` binding of `callback`.
+     * @returns {Array} Returns a new array of sorted elements.
+     * @example
+     *
+     * _.sortBy([1, 2, 3], function(num) { return Math.sin(num); });
+     * // => [3, 1, 2]
+     *
+     * _.sortBy([1, 2, 3], function(num) { return this.sin(num); }, Math);
+     * // => [3, 1, 2]
+     *
+     * var characters = [
+     *   { 'name': 'barney',  'age': 36 },
+     *   { 'name': 'fred',    'age': 40 },
+     *   { 'name': 'barney',  'age': 26 },
+     *   { 'name': 'fred',    'age': 30 }
+     * ];
+     *
+     * // using "_.pluck" callback shorthand
+     * _.map(_.sortBy(characters, 'age'), _.values);
+     * // => [['barney', 26], ['fred', 30], ['barney', 36], ['fred', 40]]
+     *
+     * // sorting by multiple properties
+     * _.map(_.sortBy(characters, ['name', 'age']), _.values);
+     * // = > [['barney', 26], ['barney', 36], ['fred', 30], ['fred', 40]]
+     */
+    function sortBy(collection, callback, thisArg) {
+      var index = -1,
+          isArr = isArray(callback),
+          length = collection ? collection.length : 0,
+          result = Array(typeof length == 'number' ? length : 0);
+
+      if (!isArr) {
+        callback = lodash.createCallback(callback, thisArg, 3);
+      }
+      forEach(collection, function(value, key, collection) {
+        var object = result[++index] = getObject();
+        if (isArr) {
+          object.criteria = map(callback, function(key) { return value[key]; });
+        } else {
+          (object.criteria = getArray())[0] = callback(value, key, collection);
+        }
+        object.index = index;
+        object.value = value;
+      });
+
+      length = result.length;
+      result.sort(compareAscending);
+      while (length--) {
+        var object = result[length];
+        result[length] = object.value;
+        if (!isArr) {
+          releaseArray(object.criteria);
+        }
+        releaseObject(object);
+      }
+      return result;
+    }
+
+    /**
+     * Converts the `collection` to an array.
+     *
+     * @static
+     * @memberOf _
+     * @category Collections
+     * @param {Array|Object|string} collection The collection to convert.
+     * @returns {Array} Returns the new converted array.
+     * @example
+     *
+     * (function() { return _.toArray(arguments).slice(1); })(1, 2, 3, 4);
+     * // => [2, 3, 4]
+     */
+    function toArray(collection) {
+      if (collection && typeof collection.length == 'number') {
+        return slice(collection);
+      }
+      return values(collection);
+    }
+
+    /**
+     * Performs a deep comparison of each element in a `collection` to the given
+     * `properties` object, returning an array of all elements that have equivalent
+     * property values.
+     *
+     * @static
+     * @memberOf _
+     * @type Function
+     * @category Collections
+     * @param {Array|Object|string} collection The collection to iterate over.
+     * @param {Object} props The object of property values to filter by.
+     * @returns {Array} Returns a new array of elements that have the given properties.
+     * @example
+     *
+     * var characters = [
+     *   { 'name': 'barney', 'age': 36, 'pets': ['hoppy'] },
+     *   { 'name': 'fred',   'age': 40, 'pets': ['baby puss', 'dino'] }
+     * ];
+     *
+     * _.where(characters, { 'age': 36 });
+     * // => [{ 'name': 'barney', 'age': 36, 'pets': ['hoppy'] }]
+     *
+     * _.where(characters, { 'pets': ['dino'] });
+     * // => [{ 'name': 'fred', 'age': 40, 'pets': ['baby puss', 'dino'] }]
+     */
+    var where = filter;
+
+    /*--------------------------------------------------------------------------*/
+
+    /**
+     * Creates an array with all falsey values removed. The values `false`, `null`,
+     * `0`, `""`, `undefined`, and `NaN` are all falsey.
+     *
+     * @static
+     * @memberOf _
+     * @category Arrays
+     * @param {Array} array The array to compact.
+     * @returns {Array} Returns a new array of filtered values.
+     * @example
+     *
+     * _.compact([0, 1, false, 2, '', 3]);
+     * // => [1, 2, 3]
+     */
+    function compact(array) {
+      var index = -1,
+          length = array ? array.length : 0,
+          result = [];
+
+      while (++index < length) {
+        var value = array[index];
+        if (value) {
+          result.push(value);
+        }
+      }
+      return result;
+    }
+
+    /**
+     * Creates an array excluding all values of the provided arrays using strict
+     * equality for comparisons, i.e. `===`.
+     *
+     * @static
+     * @memberOf _
+     * @category Arrays
+     * @param {Array} array The array to process.
+     * @param {...Array} [values] The arrays of values to exclude.
+     * @returns {Array} Returns a new array of filtered values.
+     * @example
+     *
+     * _.difference([1, 2, 3, 4, 5], [5, 2, 10]);
+     * // => [1, 3, 4]
+     */
+    function difference(array) {
+      return baseDifference(array, baseFlatten(arguments, true, true, 1));
+    }
+
+    /**
+     * This method is like `_.find` except that it returns the index of the first
+     * element that passes the callback check, instead of the element itself.
+     *
+     * If a property name is provided for `callback` the created "_.pluck" style
+     * callback will return the property value of the given element.
+     *
+     * If an object is provided for `callback` the created "_.where" style callback
+     * will return `true` for elements that have the properties of the given object,
+     * else `false`.
+     *
+     * @static
+     * @memberOf _
+     * @category Arrays
+     * @param {Array} array The array to search.
+     * @param {Function|Object|string} [callback=identity] The function called
+     *  per iteration. If a property name or object is provided it will be used
+     *  to create a "_.pluck" or "_.where" style callback, respectively.
+     * @param {*} [thisArg] The `this` binding of `callback`.
+     * @returns {number} Returns the index of the found element, else `-1`.
+     * @example
+     *
+     * var characters = [
+     *   { 'name': 'barney',  'age': 36, 'blocked': false },
+     *   { 'name': 'fred',    'age': 40, 'blocked': true },
+     *   { 'name': 'pebbles', 'age': 1,  'blocked': false }
+     * ];
+     *
+     * _.findIndex(characters, function(chr) {
+     *   return chr.age < 20;
+     * });
+     * // => 2
+     *
+     * // using "_.where" callback shorthand
+     * _.findIndex(characters, { 'age': 36 });
+     * // => 0
+     *
+     * // using "_.pluck" callback shorthand
+     * _.findIndex(characters, 'blocked');
+     * // => 1
+     */
+    function findIndex(array, callback, thisArg) {
+      var index = -1,
+          length = array ? array.length : 0;
+
+      callback = lodash.createCallback(callback, thisArg, 3);
+      while (++index < length) {
+        if (callback(array[index], index, array)) {
+          return index;
+        }
+      }
+      return -1;
+    }
+
+    /**
+     * This method is like `_.findIndex` except that it iterates over elements
+     * of a `collection` from right to left.
+     *
+     * If a property name is provided for `callback` the created "_.pluck" style
+     * callback will return the property value of the given element.
+     *
+     * If an object is provided for `callback` the created "_.where" style callback
+     * will return `true` for elements that have the properties of the given object,
+     * else `false`.
+     *
+     * @static
+     * @memberOf _
+     * @category Arrays
+     * @param {Array} array The array to search.
+     * @param {Function|Object|string} [callback=identity] The function called
+     *  per iteration. If a property name or object is provided it will be used
+     *  to create a "_.pluck" or "_.where" style callback, respectively.
+     * @param {*} [thisArg] The `this` binding of `callback`.
+     * @returns {number} Returns the index of the found element, else `-1`.
+     * @example
+     *
+     * var characters = [
+     *   { 'name': 'barney',  'age': 36, 'blocked': true },
+     *   { 'name': 'fred',    'age': 40, 'blocked': false },
+     *   { 'name': 'pebbles', 'age': 1,  'blocked': true }
+     * ];
+     *
+     * _.findLastIndex(characters, function(chr) {
+     *   return chr.age > 30;
+     * });
+     * // => 1
+     *
+     * // using "_.where" callback shorthand
+     * _.findLastIndex(characters, { 'age': 36 });
+     * // => 0
+     *
+     * // using "_.pluck" callback shorthand
+     * _.findLastIndex(characters, 'blocked');
+     * // => 2
+     */
+    function findLastIndex(array, callback, thisArg) {
+      var length = array ? array.length : 0;
+      callback = lodash.createCallback(callback, thisArg, 3);
+      while (length--) {
+        if (callback(array[length], length, array)) {
+          return length;
+        }
+      }
+      return -1;
+    }
+
+    /**
+     * Gets the first element or first `n` elements of an array. If a callback
+     * is provided elements at the beginning of the array are returned as long
+     * as the callback returns truey. The callback is bound to `thisArg` and
+     * invoked with three arguments; (value, index, array).
+     *
+     * If a property name is provided for `callback` the created "_.pluck" style
+     * callback will return the property value of the given element.
+     *
+     * If an object is provided for `callback` the created "_.where" style callback
+     * will return `true` for elements that have the properties of the given object,
+     * else `false`.
+     *
+     * @static
+     * @memberOf _
+     * @alias head, take
+     * @category Arrays
+     * @param {Array} array The array to query.
+     * @param {Function|Object|number|string} [callback] The function called
+     *  per element or the number of elements to return. If a property name or
+     *  object is provided it will be used to create a "_.pluck" or "_.where"
+     *  style callback, respectively.
+     * @param {*} [thisArg] The `this` binding of `callback`.
+     * @returns {*} Returns the first element(s) of `array`.
+     * @example
+     *
+     * _.first([1, 2, 3]);
+     * // => 1
+     *
+     * _.first([1, 2, 3], 2);
+     * // => [1, 2]
+     *
+     * _.first([1, 2, 3], function(num) {
+     *   return num < 3;
+     * });
+     * // => [1, 2]
+     *
+     * var characters = [
+     *   { 'name': 'barney',  'blocked': true,  'employer': 'slate' },
+     *   { 'name': 'fred',    'blocked': false, 'employer': 'slate' },
+     *   { 'name': 'pebbles', 'blocked': true,  'employer': 'na' }
+     * ];
+     *
+     * // using "_.pluck" callback shorthand
+     * _.first(characters, 'blocked');
+     * // => [{ 'name': 'barney', 'blocked': true, 'employer': 'slate' }]
+     *
+     * // using "_.where" callback shorthand
+     * _.pluck(_.first(characters, { 'employer': 'slate' }), 'name');
+     * // => ['barney', 'fred']
+     */
+    function first(array, callback, thisArg) {
+      var n = 0,
+          length = array ? array.length : 0;
+
+      if (typeof callback != 'number' && callback != null) {
+        var index = -1;
+        callback = lodash.createCallback(callback, thisArg, 3);
+        while (++index < length && callback(array[index], index, array)) {
+          n++;
+        }
+      } else {
+        n = callback;
+        if (n == null || thisArg) {
+          return array ? array[0] : undefined;
+        }
+      }
+      return slice(array, 0, nativeMin(nativeMax(0, n), length));
+    }
+
+    /**
+     * Flattens a nested array (the nesting can be to any depth). If `isShallow`
+     * is truey, the array will only be flattened a single level. If a callback
+     * is provided each element of the array is passed through the callback before
+     * flattening. The callback is bound to `thisArg` and invoked with three
+     * arguments; (value, index, array).
+     *
+     * If a property name is provided for `callback` the created "_.pluck" style
+     * callback will return the property value of the given element.
+     *
+     * If an object is provided for `callback` the created "_.where" style callback
+     * will return `true` for elements that have the properties of the given object,
+     * else `false`.
+     *
+     * @static
+     * @memberOf _
+     * @category Arrays
+     * @param {Array} array The array to flatten.
+     * @param {boolean} [isShallow=false] A flag to restrict flattening to a single level.
+     * @param {Function|Object|string} [callback=identity] The function called
+     *  per iteration. If a property name or object is provided it will be used
+     *  to create a "_.pluck" or "_.where" style callback, respectively.
+     * @param {*} [thisArg] The `this` binding of `callback`.
+     * @returns {Array} Returns a new flattened array.
+     * @example
+     *
+     * _.flatten([1, [2], [3, [[4]]]]);
+     * // => [1, 2, 3, 4];
+     *
+     * _.flatten([1, [2], [3, [[4]]]], true);
+     * // => [1, 2, 3, [[4]]];
+     *
+     * var characters = [
+     *   { 'name': 'barney', 'age': 30, 'pets': ['hoppy'] },
+     *   { 'name': 'fred',   'age': 40, 'pets': ['baby puss', 'dino'] }
+     * ];
+     *
+     * // using "_.pluck" callback shorthand
+     * _.flatten(characters, 'pets');
+     * // => ['hoppy', 'baby puss', 'dino']
+     */
+    function flatten(array, isShallow, callback, thisArg) {
+      // juggle arguments
+      if (typeof isShallow != 'boolean' && isShallow != null) {
+        thisArg = callback;
+        callback = (typeof isShallow != 'function' && thisArg && thisArg[isShallow] === array) ? null : isShallow;
+        isShallow = false;
+      }
+      if (callback != null) {
+        array = map(array, callback, thisArg);
+      }
+      return baseFlatten(array, isShallow);
+    }
+
+    /**
+     * Gets the index at which the first occurrence of `value` is found using
+     * strict equality for comparisons, i.e. `===`. If the array is already sorted
+     * providing `true` for `fromIndex` will run a faster binary search.
+     *
+     * @static
+     * @memberOf _
+     * @category Arrays
+     * @param {Array} array The array to search.
+     * @param {*} value The value to search for.
+     * @param {boolean|number} [fromIndex=0] The index to search from or `true`
+     *  to perform a binary search on a sorted array.
+     * @returns {number} Returns the index of the matched value or `-1`.
+     * @example
+     *
+     * _.indexOf([1, 2, 3, 1, 2, 3], 2);
+     * // => 1
+     *
+     * _.indexOf([1, 2, 3, 1, 2, 3], 2, 3);
+     * // => 4
+     *
+     * _.indexOf([1, 1, 2, 2, 3, 3], 2, true);
+     * // => 2
+     */
+    function indexOf(array, value, fromIndex) {
+      if (typeof fromIndex == 'number') {
+        var length = array ? array.length : 0;
+        fromIndex = (fromIndex < 0 ? nativeMax(0, length + fromIndex) : fromIndex || 0);
+      } else if (fromIndex) {
+        var index = sortedIndex(array, value);
+        return array[index] === value ? index : -1;
+      }
+      return baseIndexOf(array, value, fromIndex);
+    }
+
+    /**
+     * Gets all but the last element or last `n` elements of an array. If a
+     * callback is provided elements at the end of the array are excluded from
+     * the result as long as the callback returns truey. The callback is bound
+     * to `thisArg` and invoked with three arguments; (value, index, array).
+     *
+     * If a property name is provided for `callback` the created "_.pluck" style
+     * callback will return the property value of the given element.
+     *
+     * If an object is provided for `callback` the created "_.where" style callback
+     * will return `true` for elements that have the properties of the given object,
+     * else `false`.
+     *
+     * @static
+     * @memberOf _
+     * @category Arrays
+     * @param {Array} array The array to query.
+     * @param {Function|Object|number|string} [callback=1] The function called
+     *  per element or the number of elements to exclude. If a property name or
+     *  object is provided it will be used to create a "_.pluck" or "_.where"
+     *  style callback, respectively.
+     * @param {*} [thisArg] The `this` binding of `callback`.
+     * @returns {Array} Returns a slice of `array`.
+     * @example
+     *
+     * _.initial([1, 2, 3]);
+     * // => [1, 2]
+     *
+     * _.initial([1, 2, 3], 2);
+     * // => [1]
+     *
+     * _.initial([1, 2, 3], function(num) {
+     *   return num > 1;
+     * });
+     * // => [1]
+     *
+     * var characters = [
+     *   { 'name': 'barney',  'blocked': false, 'employer': 'slate' },
+     *   { 'name': 'fred',    'blocked': true,  'employer': 'slate' },
+     *   { 'name': 'pebbles', 'blocked': true,  'employer': 'na' }
+     * ];
+     *
+     * // using "_.pluck" callback shorthand
+     * _.initial(characters, 'blocked');
+     * // => [{ 'name': 'barney',  'blocked': false, 'employer': 'slate' }]
+     *
+     * // using "_.where" callback shorthand
+     * _.pluck(_.initial(characters, { 'employer': 'na' }), 'name');
+     * // => ['barney', 'fred']
+     */
+    function initial(array, callback, thisArg) {
+      var n = 0,
+          length = array ? array.length : 0;
+
+      if (typeof callback != 'number' && callback != null) {
+        var index = length;
+        callback = lodash.createCallback(callback, thisArg, 3);
+        while (index-- && callback(array[index], index, array)) {
+          n++;
+        }
+      } else {
+        n = (callback == null || thisArg) ? 1 : callback || n;
+      }
+      return slice(array, 0, nativeMin(nativeMax(0, length - n), length));
+    }
+
+    /**
+     * Creates an array of unique values present in all provided arrays using
+     * strict equality for comparisons, i.e. `===`.
+     *
+     * @static
+     * @memberOf _
+     * @category Arrays
+     * @param {...Array} [array] The arrays to inspect.
+     * @returns {Array} Returns an array of shared values.
+     * @example
+     *
+     * _.intersection([1, 2, 3], [5, 2, 1, 4], [2, 1]);
+     * // => [1, 2]
+     */
+    function intersection() {
+      var args = [],
+          argsIndex = -1,
+          argsLength = arguments.length,
+          caches = getArray(),
+          indexOf = getIndexOf(),
+          trustIndexOf = indexOf === baseIndexOf,
+          seen = getArray();
+
+      while (++argsIndex < argsLength) {
+        var value = arguments[argsIndex];
+        if (isArray(value) || isArguments(value)) {
+          args.push(value);
+          caches.push(trustIndexOf && value.length >= largeArraySize &&
+            createCache(argsIndex ? args[argsIndex] : seen));
+        }
+      }
+      var array = args[0],
+          index = -1,
+          length = array ? array.length : 0,
+          result = [];
+
+      outer:
+      while (++index < length) {
+        var cache = caches[0];
+        value = array[index];
+
+        if ((cache ? cacheIndexOf(cache, value) : indexOf(seen, value)) < 0) {
+          argsIndex = argsLength;
+          (cache || seen).push(value);
+          while (--argsIndex) {
+            cache = caches[argsIndex];
+            if ((cache ? cacheIndexOf(cache, value) : indexOf(args[argsIndex], value)) < 0) {
+              continue outer;
+            }
+          }
+          result.push(value);
+        }
+      }
+      while (argsLength--) {
+        cache = caches[argsLength];
+        if (cache) {
+          releaseObject(cache);
+        }
+      }
+      releaseArray(caches);
+      releaseArray(seen);
+      return result;
+    }
+
+    /**
+     * Gets the last element or last `n` elements of an array. If a callback is
+     * provided elements at the end of the array are returned as long as the
+     * callback returns truey. The callback is bound to `thisArg` and invoked
+     * with three arguments; (value, index, array).
+     *
+     * If a property name is provided for `callback` the created "_.pluck" style
+     * callback will return the property value of the given element.
+     *
+     * If an object is provided for `callback` the created "_.where" style callback
+     * will return `true` for elements that have the properties of the given object,
+     * else `false`.
+     *
+     * @static
+     * @memberOf _
+     * @category Arrays
+     * @param {Array} array The array to query.
+     * @param {Function|Object|number|string} [callback] The function called
+     *  per element or the number of elements to return. If a property name or
+     *  object is provided it will be used to create a "_.pluck" or "_.where"
+     *  style callback, respectively.
+     * @param {*} [thisArg] The `this` binding of `callback`.
+     * @returns {*} Returns the last element(s) of `array`.
+     * @example
+     *
+     * _.last([1, 2, 3]);
+     * // => 3
+     *
+     * _.last([1, 2, 3], 2);
+     * // => [2, 3]
+     *
+     * _.last([1, 2, 3], function(num) {
+     *   return num > 1;
+     * });
+     * // => [2, 3]
+     *
+     * var characters = [
+     *   { 'name': 'barney',  'blocked': false, 'employer': 'slate' },
+     *   { 'name': 'fred',    'blocked': true,  'employer': 'slate' },
+     *   { 'name': 'pebbles', 'blocked': true,  'employer': 'na' }
+     * ];
+     *
+     * // using "_.pluck" callback shorthand
+     * _.pluck(_.last(characters, 'blocked'), 'name');
+     * // => ['fred', 'pebbles']
+     *
+     * // using "_.where" callback shorthand
+     * _.last(characters, { 'employer': 'na' });
+     * // => [{ 'name': 'pebbles', 'blocked': true, 'employer': 'na' }]
+     */
+    function last(array, callback, thisArg) {
+      var n = 0,
+          length = array ? array.length : 0;
+
+      if (typeof callback != 'number' && callback != null) {
+        var index = length;
+        callback = lodash.createCallback(callback, thisArg, 3);
+        while (index-- && callback(array[index], index, array)) {
+          n++;
+        }
+      } else {
+        n = callback;
+        if (n == null || thisArg) {
+          return array ? array[length - 1] : undefined;
+        }
+      }
+      return slice(array, nativeMax(0, length - n));
+    }
+
+    /**
+     * Gets the index at which the last occurrence of `value` is found using strict
+     * equality for comparisons, i.e. `===`. If `fromIndex` is negative, it is used
+     * as the offset from the end of the collection.
+     *
+     * If a property name is provided for `callback` the created "_.pluck" style
+     * callback will return the property value of the given element.
+     *
+     * If an object is provided for `callback` the created "_.where" style callback
+     * will return `true` for elements that have the properties of the given object,
+     * else `false`.
+     *
+     * @static
+     * @memberOf _
+     * @category Arrays
+     * @param {Array} array The array to search.
+     * @param {*} value The value to search for.
+     * @param {number} [fromIndex=array.length-1] The index to search from.
+     * @returns {number} Returns the index of the matched value or `-1`.
+     * @example
+     *
+     * _.lastIndexOf([1, 2, 3, 1, 2, 3], 2);
+     * // => 4
+     *
+     * _.lastIndexOf([1, 2, 3, 1, 2, 3], 2, 3);
+     * // => 1
+     */
+    function lastIndexOf(array, value, fromIndex) {
+      var index = array ? array.length : 0;
+      if (typeof fromIndex == 'number') {
+        index = (fromIndex < 0 ? nativeMax(0, index + fromIndex) : nativeMin(fromIndex, index - 1)) + 1;
+      }
+      while (index--) {
+        if (array[index] === value) {
+          return index;
+        }
+      }
+      return -1;
+    }
+
+    /**
+     * Removes all provided values from the given array using strict equality for
+     * comparisons, i.e. `===`.
+     *
+     * @static
+     * @memberOf _
+     * @category Arrays
+     * @param {Array} array The array to modify.
+     * @param {...*} [value] The values to remove.
+     * @returns {Array} Returns `array`.
+     * @example
+     *
+     * var array = [1, 2, 3, 1, 2, 3];
+     * _.pull(array, 2, 3);
+     * console.log(array);
+     * // => [1, 1]
+     */
+    function pull(array) {
+      var args = arguments,
+          argsIndex = 0,
+          argsLength = args.length,
+          length = array ? array.length : 0;
+
+      while (++argsIndex < argsLength) {
+        var index = -1,
+            value = args[argsIndex];
+        while (++index < length) {
+          if (array[index] === value) {
+            splice.call(array, index--, 1);
+            length--;
+          }
+        }
+      }
+      return array;
+    }
+
+    /**
+     * Creates an array of numbers (positive and/or negative) progressing from
+     * `start` up to but not including `end`. If `start` is less than `stop` a
+     * zero-length range is created unless a negative `step` is specified.
+     *
+     * @static
+     * @memberOf _
+     * @category Arrays
+     * @param {number} [start=0] The start of the range.
+     * @param {number} end The end of the range.
+     * @param {number} [step=1] The value to increment or decrement by.
+     * @returns {Array} Returns a new range array.
+     * @example
+     *
+     * _.range(4);
+     * // => [0, 1, 2, 3]
+     *
+     * _.range(1, 5);
+     * // => [1, 2, 3, 4]
+     *
+     * _.range(0, 20, 5);
+     * // => [0, 5, 10, 15]
+     *
+     * _.range(0, -4, -1);
+     * // => [0, -1, -2, -3]
+     *
+     * _.range(1, 4, 0);
+     * // => [1, 1, 1]
+     *
+     * _.range(0);
+     * // => []
+     */
+    function range(start, end, step) {
+      start = +start || 0;
+      step = typeof step == 'number' ? step : (+step || 1);
+
+      if (end == null) {
+        end = start;
+        start = 0;
+      }
+      // use `Array(length)` so engines like Chakra and V8 avoid slower modes
+      // http://youtu.be/XAqIpGU8ZZk#t=17m25s
+      var index = -1,
+          length = nativeMax(0, ceil((end - start) / (step || 1))),
+          result = Array(length);
+
+      while (++index < length) {
+        result[index] = start;
+        start += step;
+      }
+      return result;
+    }
+
+    /**
+     * Removes all elements from an array that the callback returns truey for
+     * and returns an array of removed elements. The callback is bound to `thisArg`
+     * and invoked with three arguments; (value, index, array).
+     *
+     * If a property name is provided for `callback` the created "_.pluck" style
+     * callback will return the property value of the given element.
+     *
+     * If an object is provided for `callback` the created "_.where" style callback
+     * will return `true` for elements that have the properties of the given object,
+     * else `false`.
+     *
+     * @static
+     * @memberOf _
+     * @category Arrays
+     * @param {Array} array The array to modify.
+     * @param {Function|Object|string} [callback=identity] The function called
+     *  per iteration. If a property name or object is provided it will be used
+     *  to create a "_.pluck" or "_.where" style callback, respectively.
+     * @param {*} [thisArg] The `this` binding of `callback`.
+     * @returns {Array} Returns a new array of removed elements.
+     * @example
+     *
+     * var array = [1, 2, 3, 4, 5, 6];
+     * var evens = _.remove(array, function(num) { return num % 2 == 0; });
+     *
+     * console.log(array);
+     * // => [1, 3, 5]
+     *
+     * console.log(evens);
+     * // => [2, 4, 6]
+     */
+    function remove(array, callback, thisArg) {
+      var index = -1,
+          length = array ? array.length : 0,
+          result = [];
+
+      callback = lodash.createCallback(callback, thisArg, 3);
+      while (++index < length) {
+        var value = array[index];
+        if (callback(value, index, array)) {
+          result.push(value);
+          splice.call(array, index--, 1);
+          length--;
+        }
+      }
+      return result;
+    }
+
+    /**
+     * The opposite of `_.initial` this method gets all but the first element or
+     * first `n` elements of an array. If a callback function is provided elements
+     * at the beginning of the array are excluded from the result as long as the
+     * callback returns truey. The callback is bound to `thisArg` and invoked
+     * with three arguments; (value, index, array).
+     *
+     * If a property name is provided for `callback` the created "_.pluck" style
+     * callback will return the property value of the given element.
+     *
+     * If an object is provided for `callback` the created "_.where" style callback
+     * will return `true` for elements that have the properties of the given object,
+     * else `false`.
+     *
+     * @static
+     * @memberOf _
+     * @alias drop, tail
+     * @category Arrays
+     * @param {Array} array The array to query.
+     * @param {Function|Object|number|string} [callback=1] The function called
+     *  per element or the number of elements to exclude. If a property name or
+     *  object is provided it will be used to create a "_.pluck" or "_.where"
+     *  style callback, respectively.
+     * @param {*} [thisArg] The `this` binding of `callback`.
+     * @returns {Array} Returns a slice of `array`.
+     * @example
+     *
+     * _.rest([1, 2, 3]);
+     * // => [2, 3]
+     *
+     * _.rest([1, 2, 3], 2);
+     * // => [3]
+     *
+     * _.rest([1, 2, 3], function(num) {
+     *   return num < 3;
+     * });
+     * // => [3]
+     *
+     * var characters = [
+     *   { 'name': 'barney',  'blocked': true,  'employer': 'slate' },
+     *   { 'name': 'fred',    'blocked': false,  'employer': 'slate' },
+     *   { 'name': 'pebbles', 'blocked': true, 'employer': 'na' }
+     * ];
+     *
+     * // using "_.pluck" callback shorthand
+     * _.pluck(_.rest(characters, 'blocked'), 'name');
+     * // => ['fred', 'pebbles']
+     *
+     * // using "_.where" callback shorthand
+     * _.rest(characters, { 'employer': 'slate' });
+     * // => [{ 'name': 'pebbles', 'blocked': true, 'employer': 'na' }]
+     */
+    function rest(array, callback, thisArg) {
+      if (typeof callback != 'number' && callback != null) {
+        var n = 0,
+            index = -1,
+            length = array ? array.length : 0;
+
+        callback = lodash.createCallback(callback, thisArg, 3);
+        while (++index < length && callback(array[index], index, array)) {
+          n++;
+        }
+      } else {
+        n = (callback == null || thisArg) ? 1 : nativeMax(0, callback);
+      }
+      return slice(array, n);
+    }
+
+    /**
+     * Uses a binary search to determine the smallest index at which a value
+     * should be inserted into a given sorted array in order to maintain the sort
+     * order of the array. If a callback is provided it will be executed for
+     * `value` and each element of `array` to compute their sort ranking. The
+     * callback is bound to `thisArg` and invoked with one argument; (value).
+     *
+     * If a property name is provided for `callback` the created "_.pluck" style
+     * callback will return the property value of the given element.
+     *
+     * If an object is provided for `callback` the created "_.where" style callback
+     * will return `true` for elements that have the properties of the given object,
+     * else `false`.
+     *
+     * @static
+     * @memberOf _
+     * @category Arrays
+     * @param {Array} array The array to inspect.
+     * @param {*} value The value to evaluate.
+     * @param {Function|Object|string} [callback=identity] The function called
+     *  per iteration. If a property name or object is provided it will be used
+     *  to create a "_.pluck" or "_.where" style callback, respectively.
+     * @param {*} [thisArg] The `this` binding of `callback`.
+     * @returns {number} Returns the index at which `value` should be inserted
+     *  into `array`.
+     * @example
+     *
+     * _.sortedIndex([20, 30, 50], 40);
+     * // => 2
+     *
+     * // using "_.pluck" callback shorthand
+     * _.sortedIndex([{ 'x': 20 }, { 'x': 30 }, { 'x': 50 }], { 'x': 40 }, 'x');
+     * // => 2
+     *
+     * var dict = {
+     *   'wordToNumber': { 'twenty': 20, 'thirty': 30, 'fourty': 40, 'fifty': 50 }
+     * };
+     *
+     * _.sortedIndex(['twenty', 'thirty', 'fifty'], 'fourty', function(word) {
+     *   return dict.wordToNumber[word];
+     * });
+     * // => 2
+     *
+     * _.sortedIndex(['twenty', 'thirty', 'fifty'], 'fourty', function(word) {
+     *   return this.wordToNumber[word];
+     * }, dict);
+     * // => 2
+     */
+    function sortedIndex(array, value, callback, thisArg) {
+      var low = 0,
+          high = array ? array.length : low;
+
+      // explicitly reference `identity` for better inlining in Firefox
+      callback = callback ? lodash.createCallback(callback, thisArg, 1) : identity;
+      value = callback(value);
+
+      while (low < high) {
+        var mid = (low + high) >>> 1;
+        (callback(array[mid]) < value)
+          ? low = mid + 1
+          : high = mid;
+      }
+      return low;
+    }
+
+    /**
+     * Creates an array of unique values, in order, of the provided arrays using
+     * strict equality for comparisons, i.e. `===`.
+     *
+     * @static
+     * @memberOf _
+     * @category Arrays
+     * @param {...Array} [array] The arrays to inspect.
+     * @returns {Array} Returns an array of combined values.
+     * @example
+     *
+     * _.union([1, 2, 3], [5, 2, 1, 4], [2, 1]);
+     * // => [1, 2, 3, 5, 4]
+     */
+    function union() {
+      return baseUniq(baseFlatten(arguments, true, true));
+    }
+
+    /**
+     * Creates a duplicate-value-free version of an array using strict equality
+     * for comparisons, i.e. `===`. If the array is sorted, providing
+     * `true` for `isSorted` will use a faster algorithm. If a callback is provided
+     * each element of `array` is passed through the callback before uniqueness
+     * is computed. The callback is bound to `thisArg` and invoked with three
+     * arguments; (value, index, array).
+     *
+     * If a property name is provided for `callback` the created "_.pluck" style
+     * callback will return the property value of the given element.
+     *
+     * If an object is provided for `callback` the created "_.where" style callback
+     * will return `true` for elements that have the properties of the given object,
+     * else `false`.
+     *
+     * @static
+     * @memberOf _
+     * @alias unique
+     * @category Arrays
+     * @param {Array} array The array to process.
+     * @param {boolean} [isSorted=false] A flag to indicate that `array` is sorted.
+     * @param {Function|Object|string} [callback=identity] The function called
+     *  per iteration. If a property name or object is provided it will be used
+     *  to create a "_.pluck" or "_.where" style callback, respectively.
+     * @param {*} [thisArg] The `this` binding of `callback`.
+     * @returns {Array} Returns a duplicate-value-free array.
+     * @example
+     *
+     * _.uniq([1, 2, 1, 3, 1]);
+     * // => [1, 2, 3]
+     *
+     * _.uniq([1, 1, 2, 2, 3], true);
+     * // => [1, 2, 3]
+     *
+     * _.uniq(['A', 'b', 'C', 'a', 'B', 'c'], function(letter) { return letter.toLowerCase(); });
+     * // => ['A', 'b', 'C']
+     *
+     * _.uniq([1, 2.5, 3, 1.5, 2, 3.5], function(num) { return this.floor(num); }, Math);
+     * // => [1, 2.5, 3]
+     *
+     * // using "_.pluck" callback shorthand
+     * _.uniq([{ 'x': 1 }, { 'x': 2 }, { 'x': 1 }], 'x');
+     * // => [{ 'x': 1 }, { 'x': 2 }]
+     */
+    function uniq(array, isSorted, callback, thisArg) {
+      // juggle arguments
+      if (typeof isSorted != 'boolean' && isSorted != null) {
+        thisArg = callback;
+        callback = (typeof isSorted != 'function' && thisArg && thisArg[isSorted] === array) ? null : isSorted;
+        isSorted = false;
+      }
+      if (callback != null) {
+        callback = lodash.createCallback(callback, thisArg, 3);
+      }
+      return baseUniq(array, isSorted, callback);
+    }
+
+    /**
+     * Creates an array excluding all provided values using strict equality for
+     * comparisons, i.e. `===`.
+     *
+     * @static
+     * @memberOf _
+     * @category Arrays
+     * @param {Array} array The array to filter.
+     * @param {...*} [value] The values to exclude.
+     * @returns {Array} Returns a new array of filtered values.
+     * @example
+     *
+     * _.without([1, 2, 1, 0, 3, 1, 4], 0, 1);
+     * // => [2, 3, 4]
+     */
+    function without(array) {
+      return baseDifference(array, slice(arguments, 1));
+    }
+
+    /**
+     * Creates an array that is the symmetric difference of the provided arrays.
+     * See http://en.wikipedia.org/wiki/Symmetric_difference.
+     *
+     * @static
+     * @memberOf _
+     * @category Arrays
+     * @param {...Array} [array] The arrays to inspect.
+     * @returns {Array} Returns an array of values.
+     * @example
+     *
+     * _.xor([1, 2, 3], [5, 2, 1, 4]);
+     * // => [3, 5, 4]
+     *
+     * _.xor([1, 2, 5], [2, 3, 5], [3, 4, 5]);
+     * // => [1, 4, 5]
+     */
+    function xor() {
+      var index = -1,
+          length = arguments.length;
+
+      while (++index < length) {
+        var array = arguments[index];
+        if (isArray(array) || isArguments(array)) {
+          var result = result
+            ? baseUniq(baseDifference(result, array).concat(baseDifference(array, result)))
+            : array;
+        }
+      }
+      return result || [];
+    }
+
+    /**
+     * Creates an array of grouped elements, the first of which contains the first
+     * elements of the given arrays, the second of which contains the second
+     * elements of the given arrays, and so on.
+     *
+     * @static
+     * @memberOf _
+     * @alias unzip
+     * @category Arrays
+     * @param {...Array} [array] Arrays to process.
+     * @returns {Array} Returns a new array of grouped elements.
+     * @example
+     *
+     * _.zip(['fred', 'barney'], [30, 40], [true, false]);
+     * // => [['fred', 30, true], ['barney', 40, false]]
+     */
+    function zip() {
+      var array = arguments.length > 1 ? arguments : arguments[0],
+          index = -1,
+          length = array ? max(pluck(array, 'length')) : 0,
+          result = Array(length < 0 ? 0 : length);
+
+      while (++index < length) {
+        result[index] = pluck(array, index);
+      }
+      return result;
+    }
+
+    /**
+     * Creates an object composed from arrays of `keys` and `values`. Provide
+     * either a single two dimensional array, i.e. `[[key1, value1], [key2, value2]]`
+     * or two arrays, one of `keys` and one of corresponding `values`.
+     *
+     * @static
+     * @memberOf _
+     * @alias object
+     * @category Arrays
+     * @param {Array} keys The array of keys.
+     * @param {Array} [values=[]] The array of values.
+     * @returns {Object} Returns an object composed of the given keys and
+     *  corresponding values.
+     * @example
+     *
+     * _.zipObject(['fred', 'barney'], [30, 40]);
+     * // => { 'fred': 30, 'barney': 40 }
+     */
+    function zipObject(keys, values) {
+      var index = -1,
+          length = keys ? keys.length : 0,
+          result = {};
+
+      if (!values && length && !isArray(keys[0])) {
+        values = [];
+      }
+      while (++index < length) {
+        var key = keys[index];
+        if (values) {
+          result[key] = values[index];
+        } else if (key) {
+          result[key[0]] = key[1];
+        }
+      }
+      return result;
+    }
+
+    /*--------------------------------------------------------------------------*/
+
+    /**
+     * Creates a function that executes `func`, with  the `this` binding and
+     * arguments of the created function, only after being called `n` times.
+     *
+     * @static
+     * @memberOf _
+     * @category Functions
+     * @param {number} n The number of times the function must be called before
+     *  `func` is executed.
+     * @param {Function} func The function to restrict.
+     * @returns {Function} Returns the new restricted function.
+     * @example
+     *
+     * var saves = ['profile', 'settings'];
+     *
+     * var done = _.after(saves.length, function() {
+     *   console.log('Done saving!');
+     * });
+     *
+     * _.forEach(saves, function(type) {
+     *   asyncSave({ 'type': type, 'complete': done });
+     * });
+     * // => logs 'Done saving!', after all saves have completed
+     */
+    function after(n, func) {
+      if (!isFunction(func)) {
+        throw new TypeError;
+      }
+      return function() {
+        if (--n < 1) {
+          return func.apply(this, arguments);
+        }
+      };
+    }
+
+    /**
+     * Creates a function that, when called, invokes `func` with the `this`
+     * binding of `thisArg` and prepends any additional `bind` arguments to those
+     * provided to the bound function.
+     *
+     * @static
+     * @memberOf _
+     * @category Functions
+     * @param {Function} func The function to bind.
+     * @param {*} [thisArg] The `this` binding of `func`.
+     * @param {...*} [arg] Arguments to be partially applied.
+     * @returns {Function} Returns the new bound function.
+     * @example
+     *
+     * var func = function(greeting) {
+     *   return greeting + ' ' + this.name;
+     * };
+     *
+     * func = _.bind(func, { 'name': 'fred' }, 'hi');
+     * func();
+     * // => 'hi fred'
+     */
+    function bind(func, thisArg) {
+      return arguments.length > 2
+        ? createWrapper(func, 17, slice(arguments, 2), null, thisArg)
+        : createWrapper(func, 1, null, null, thisArg);
+    }
+
+    /**
+     * Binds methods of an object to the object itself, overwriting the existing
+     * method. Method names may be specified as individual arguments or as arrays
+     * of method names. If no method names are provided all the function properties
+     * of `object` will be bound.
+     *
+     * @static
+     * @memberOf _
+     * @category Functions
+     * @param {Object} object The object to bind and assign the bound methods to.
+     * @param {...string} [methodName] The object method names to
+     *  bind, specified as individual method names or arrays of method names.
+     * @returns {Object} Returns `object`.
+     * @example
+     *
+     * var view = {
+     *   'label': 'docs',
+     *   'onClick': function() { console.log('clicked ' + this.label); }
+     * };
+     *
+     * _.bindAll(view);
+     * jQuery('#docs').on('click', view.onClick);
+     * // => logs 'clicked docs', when the button is clicked
+     */
+    function bindAll(object) {
+      var funcs = arguments.length > 1 ? baseFlatten(arguments, true, false, 1) : functions(object),
+          index = -1,
+          length = funcs.length;
+
+      while (++index < length) {
+        var key = funcs[index];
+        object[key] = createWrapper(object[key], 1, null, null, object);
+      }
+      return object;
+    }
+
+    /**
+     * Creates a function that, when called, invokes the method at `object[key]`
+     * and prepends any additional `bindKey` arguments to those provided to the bound
+     * function. This method differs from `_.bind` by allowing bound functions to
+     * reference methods that will be redefined or don't yet exist.
+     * See http://michaux.ca/articles/lazy-function-definition-pattern.
+     *
+     * @static
+     * @memberOf _
+     * @category Functions
+     * @param {Object} object The object the method belongs to.
+     * @param {string} key The key of the method.
+     * @param {...*} [arg] Arguments to be partially applied.
+     * @returns {Function} Returns the new bound function.
+     * @example
+     *
+     * var object = {
+     *   'name': 'fred',
+     *   'greet': function(greeting) {
+     *     return greeting + ' ' + this.name;
+     *   }
+     * };
+     *
+     * var func = _.bindKey(object, 'greet', 'hi');
+     * func();
+     * // => 'hi fred'
+     *
+     * object.greet = function(greeting) {
+     *   return greeting + 'ya ' + this.name + '!';
+     * };
+     *
+     * func();
+     * // => 'hiya fred!'
+     */
+    function bindKey(object, key) {
+      return arguments.length > 2
+        ? createWrapper(key, 19, slice(arguments, 2), null, object)
+        : createWrapper(key, 3, null, null, object);
+    }
+
+    /**
+     * Creates a function that is the composition of the provided functions,
+     * where each function consumes the return value of the function that follows.
+     * For example, composing the functions `f()`, `g()`, and `h()` produces `f(g(h()))`.
+     * Each function is executed with the `this` binding of the composed function.
+     *
+     * @static
+     * @memberOf _
+     * @category Functions
+     * @param {...Function} [func] Functions to compose.
+     * @returns {Function} Returns the new composed function.
+     * @example
+     *
+     * var realNameMap = {
+     *   'pebbles': 'penelope'
+     * };
+     *
+     * var format = function(name) {
+     *   name = realNameMap[name.toLowerCase()] || name;
+     *   return name.charAt(0).toUpperCase() + name.slice(1).toLowerCase();
+     * };
+     *
+     * var greet = function(formatted) {
+     *   return 'Hiya ' + formatted + '!';
+     * };
+     *
+     * var welcome = _.compose(greet, format);
+     * welcome('pebbles');
+     * // => 'Hiya Penelope!'
+     */
+    function compose() {
+      var funcs = arguments,
+          length = funcs.length;
+
+      while (length--) {
+        if (!isFunction(funcs[length])) {
+          throw new TypeError;
+        }
+      }
+      return function() {
+        var args = arguments,
+            length = funcs.length;
+
+        while (length--) {
+          args = [funcs[length].apply(this, args)];
+        }
+        return args[0];
+      };
+    }
+
+    /**
+     * Creates a function which accepts one or more arguments of `func` that when
+     * invoked either executes `func` returning its result, if all `func` arguments
+     * have been provided, or returns a function that accepts one or more of the
+     * remaining `func` arguments, and so on. The arity of `func` can be specified
+     * if `func.length` is not sufficient.
+     *
+     * @static
+     * @memberOf _
+     * @category Functions
+     * @param {Function} func The function to curry.
+     * @param {number} [arity=func.length] The arity of `func`.
+     * @returns {Function} Returns the new curried function.
+     * @example
+     *
+     * var curried = _.curry(function(a, b, c) {
+     *   console.log(a + b + c);
+     * });
+     *
+     * curried(1)(2)(3);
+     * // => 6
+     *
+     * curried(1, 2)(3);
+     * // => 6
+     *
+     * curried(1, 2, 3);
+     * // => 6
+     */
+    function curry(func, arity) {
+      arity = typeof arity == 'number' ? arity : (+arity || func.length);
+      return createWrapper(func, 4, null, null, null, arity);
+    }
+
+    /**
+     * Creates a function that will delay the execution of `func` until after
+     * `wait` milliseconds have elapsed since the last time it was invoked.
+     * Provide an options object to indicate that `func` should be invoked on
+     * the leading and/or trailing edge of the `wait` timeout. Subsequent calls
+     * to the debounced function will return the result of the last `func` call.
+     *
+     * Note: If `leading` and `trailing` options are `true` `func` will be called
+     * on the trailing edge of the timeout only if the the debounced function is
+     * invoked more than once during the `wait` timeout.
+     *
+     * @static
+     * @memberOf _
+     * @category Functions
+     * @param {Function} func The function to debounce.
+     * @param {number} wait The number of milliseconds to delay.
+     * @param {Object} [options] The options object.
+     * @param {boolean} [options.leading=false] Specify execution on the leading edge of the timeout.
+     * @param {number} [options.maxWait] The maximum time `func` is allowed to be delayed before it's called.
+     * @param {boolean} [options.trailing=true] Specify execution on the trailing edge of the timeout.
+     * @returns {Function} Returns the new debounced function.
+     * @example
+     *
+     * // avoid costly calculations while the window size is in flux
+     * var lazyLayout = _.debounce(calculateLayout, 150);
+     * jQuery(window).on('resize', lazyLayout);
+     *
+     * // execute `sendMail` when the click event is fired, debouncing subsequent calls
+     * jQuery('#postbox').on('click', _.debounce(sendMail, 300, {
+     *   'leading': true,
+     *   'trailing': false
+     * });
+     *
+     * // ensure `batchLog` is executed once after 1 second of debounced calls
+     * var source = new EventSource('/stream');
+     * source.addEventListener('message', _.debounce(batchLog, 250, {
+     *   'maxWait': 1000
+     * }, false);
+     */
+    function debounce(func, wait, options) {
+      var args,
+          maxTimeoutId,
+          result,
+          stamp,
+          thisArg,
+          timeoutId,
+          trailingCall,
+          lastCalled = 0,
+          maxWait = false,
+          trailing = true;
+
+      if (!isFunction(func)) {
+        throw new TypeError;
+      }
+      wait = nativeMax(0, wait) || 0;
+      if (options === true) {
+        var leading = true;
+        trailing = false;
+      } else if (isObject(options)) {
+        leading = options.leading;
+        maxWait = 'maxWait' in options && (nativeMax(wait, options.maxWait) || 0);
+        trailing = 'trailing' in options ? options.trailing : trailing;
+      }
+      var delayed = function() {
+        var remaining = wait - (now() - stamp);
+        if (remaining <= 0) {
+          if (maxTimeoutId) {
+            clearTimeout(maxTimeoutId);
+          }
+          var isCalled = trailingCall;
+          maxTimeoutId = timeoutId = trailingCall = undefined;
+          if (isCalled) {
+            lastCalled = now();
+            result = func.apply(thisArg, args);
+            if (!timeoutId && !maxTimeoutId) {
+              args = thisArg = null;
+            }
+          }
+        } else {
+          timeoutId = setTimeout(delayed, remaining);
+        }
+      };
+
+      var maxDelayed = function() {
+        if (timeoutId) {
+          clearTimeout(timeoutId);
+        }
+        maxTimeoutId = timeoutId = trailingCall = undefined;
+        if (trailing || (maxWait !== wait)) {
+          lastCalled = now();
+          result = func.apply(thisArg, args);
+          if (!timeoutId && !maxTimeoutId) {
+            args = thisArg = null;
+          }
+        }
+      };
+
+      return function() {
+        args = arguments;
+        stamp = now();
+        thisArg = this;
+        trailingCall = trailing && (timeoutId || !leading);
+
+        if (maxWait === false) {
+          var leadingCall = leading && !timeoutId;
+        } else {
+          if (!maxTimeoutId && !leading) {
+            lastCalled = stamp;
+          }
+          var remaining = maxWait - (stamp - lastCalled),
+              isCalled = remaining <= 0;
+
+          if (isCalled) {
+            if (maxTimeoutId) {
+              maxTimeoutId = clearTimeout(maxTimeoutId);
+            }
+            lastCalled = stamp;
+            result = func.apply(thisArg, args);
+          }
+          else if (!maxTimeoutId) {
+            maxTimeoutId = setTimeout(maxDelayed, remaining);
+          }
+        }
+        if (isCalled && timeoutId) {
+          timeoutId = clearTimeout(timeoutId);
+        }
+        else if (!timeoutId && wait !== maxWait) {
+          timeoutId = setTimeout(delayed, wait);
+        }
+        if (leadingCall) {
+          isCalled = true;
+          result = func.apply(thisArg, args);
+        }
+        if (isCalled && !timeoutId && !maxTimeoutId) {
+          args = thisArg = null;
+        }
+        return result;
+      };
+    }
+
+    /**
+     * Defers executing the `func` function until the current call stack has cleared.
+     * Additional arguments will be provided to `func` when it is invoked.
+     *
+     * @static
+     * @memberOf _
+     * @category Functions
+     * @param {Function} func The function to defer.
+     * @param {...*} [arg] Arguments to invoke the function with.
+     * @returns {number} Returns the timer id.
+     * @example
+     *
+     * _.defer(function(text) { console.log(text); }, 'deferred');
+     * // logs 'deferred' after one or more milliseconds
+     */
+    function defer(func) {
+      if (!isFunction(func)) {
+        throw new TypeError;
+      }
+      var args = slice(arguments, 1);
+      return setTimeout(function() { func.apply(undefined, args); }, 1);
+    }
+
+    /**
+     * Executes the `func` function after `wait` milliseconds. Additional arguments
+     * will be provided to `func` when it is invoked.
+     *
+     * @static
+     * @memberOf _
+     * @category Functions
+     * @param {Function} func The function to delay.
+     * @param {number} wait The number of milliseconds to delay execution.
+     * @param {...*} [arg] Arguments to invoke the function with.
+     * @returns {number} Returns the timer id.
+     * @example
+     *
+     * _.delay(function(text) { console.log(text); }, 1000, 'later');
+     * // => logs 'later' after one second
+     */
+    function delay(func, wait) {
+      if (!isFunction(func)) {
+        throw new TypeError;
+      }
+      var args = slice(arguments, 2);
+      return setTimeout(function() { func.apply(undefined, args); }, wait);
+    }
+
+    /**
+     * Creates a function that memoizes the result of `func`. If `resolver` is
+     * provided it will be used to determine the cache key for storing the result
+     * based on the arguments provided to the memoized function. By default, the
+     * first argument provided to the memoized function is used as the cache key.
+     * The `func` is executed with the `this` binding of the memoized function.
+     * The result cache is exposed as the `cache` property on the memoized function.
+     *
+     * @static
+     * @memberOf _
+     * @category Functions
+     * @param {Function} func The function to have its output memoized.
+     * @param {Function} [resolver] A function used to resolve the cache key.
+     * @returns {Function} Returns the new memoizing function.
+     * @example
+     *
+     * var fibonacci = _.memoize(function(n) {
+     *   return n < 2 ? n : fibonacci(n - 1) + fibonacci(n - 2);
+     * });
+     *
+     * fibonacci(9)
+     * // => 34
+     *
+     * var data = {
+     *   'fred': { 'name': 'fred', 'age': 40 },
+     *   'pebbles': { 'name': 'pebbles', 'age': 1 }
+     * };
+     *
+     * // modifying the result cache
+     * var get = _.memoize(function(name) { return data[name]; }, _.identity);
+     * get('pebbles');
+     * // => { 'name': 'pebbles', 'age': 1 }
+     *
+     * get.cache.pebbles.name = 'penelope';
+     * get('pebbles');
+     * // => { 'name': 'penelope', 'age': 1 }
+     */
+    function memoize(func, resolver) {
+      if (!isFunction(func)) {
+        throw new TypeError;
+      }
+      var memoized = function() {
+        var cache = memoized.cache,
+            key = resolver ? resolver.apply(this, arguments) : keyPrefix + arguments[0];
+
+        return hasOwnProperty.call(cache, key)
+          ? cache[key]
+          : (cache[key] = func.apply(this, arguments));
+      }
+      memoized.cache = {};
+      return memoized;
+    }
+
+    /**
+     * Creates a function that is restricted to execute `func` once. Repeat calls to
+     * the function will return the value of the first call. The `func` is executed
+     * with the `this` binding of the created function.
+     *
+     * @static
+     * @memberOf _
+     * @category Functions
+     * @param {Function} func The function to restrict.
+     * @returns {Function} Returns the new restricted function.
+     * @example
+     *
+     * var initialize = _.once(createApplication);
+     * initialize();
+     * initialize();
+     * // `initialize` executes `createApplication` once
+     */
+    function once(func) {
+      var ran,
+          result;
+
+      if (!isFunction(func)) {
+        throw new TypeError;
+      }
+      return function() {
+        if (ran) {
+          return result;
+        }
+        ran = true;
+        result = func.apply(this, arguments);
+
+        // clear the `func` variable so the function may be garbage collected
+        func = null;
+        return result;
+      };
+    }
+
+    /**
+     * Creates a function that, when called, invokes `func` with any additional
+     * `partial` arguments prepended to those provided to the new function. This
+     * method is similar to `_.bind` except it does **not** alter the `this` binding.
+     *
+     * @static
+     * @memberOf _
+     * @category Functions
+     * @param {Function} func The function to partially apply arguments to.
+     * @param {...*} [arg] Arguments to be partially applied.
+     * @returns {Function} Returns the new partially applied function.
+     * @example
+     *
+     * var greet = function(greeting, name) { return greeting + ' ' + name; };
+     * var hi = _.partial(greet, 'hi');
+     * hi('fred');
+     * // => 'hi fred'
+     */
+    function partial(func) {
+      return createWrapper(func, 16, slice(arguments, 1));
+    }
+
+    /**
+     * This method is like `_.partial` except that `partial` arguments are
+     * appended to those provided to the new function.
+     *
+     * @static
+     * @memberOf _
+     * @category Functions
+     * @param {Function} func The function to partially apply arguments to.
+     * @param {...*} [arg] Arguments to be partially applied.
+     * @returns {Function} Returns the new partially applied function.
+     * @example
+     *
+     * var defaultsDeep = _.partialRight(_.merge, _.defaults);
+     *
+     * var options = {
+     *   'variable': 'data',
+     *   'imports': { 'jq': $ }
+     * };
+     *
+     * defaultsDeep(options, _.templateSettings);
+     *
+     * options.variable
+     * // => 'data'
+     *
+     * options.imports
+     * // => { '_': _, 'jq': $ }
+     */
+    function partialRight(func) {
+      return createWrapper(func, 32, null, slice(arguments, 1));
+    }
+
+    /**
+     * Creates a function that, when executed, will only call the `func` function
+     * at most once per every `wait` milliseconds. Provide an options object to
+     * indicate that `func` should be invoked on the leading and/or trailing edge
+     * of the `wait` timeout. Subsequent calls to the throttled function will
+     * return the result of the last `func` call.
+     *
+     * Note: If `leading` and `trailing` options are `true` `func` will be called
+     * on the trailing edge of the timeout only if the the throttled function is
+     * invoked more than once during the `wait` timeout.
+     *
+     * @static
+     * @memberOf _
+     * @category Functions
+     * @param {Function} func The function to throttle.
+     * @param {number} wait The number of milliseconds to throttle executions to.
+     * @param {Object} [options] The options object.
+     * @param {boolean} [options.leading=true] Specify execution on the leading edge of the timeout.
+     * @param {boolean} [options.trailing=true] Specify execution on the trailing edge of the timeout.
+     * @returns {Function} Returns the new throttled function.
+     * @example
+     *
+     * // avoid excessively updating the position while scrolling
+     * var throttled = _.throttle(updatePosition, 100);
+     * jQuery(window).on('scroll', throttled);
+     *
+     * // execute `renewToken` when the click event is fired, but not more than once every 5 minutes
+     * jQuery('.interactive').on('click', _.throttle(renewToken, 300000, {
+     *   'trailing': false
+     * }));
+     */
+    function throttle(func, wait, options) {
+      var leading = true,
+          trailing = true;
+
+      if (!isFunction(func)) {
+        throw new TypeError;
+      }
+      if (options === false) {
+        leading = false;
+      } else if (isObject(options)) {
+        leading = 'leading' in options ? options.leading : leading;
+        trailing = 'trailing' in options ? options.trailing : trailing;
+      }
+      debounceOptions.leading = leading;
+      debounceOptions.maxWait = wait;
+      debounceOptions.trailing = trailing;
+
+      return debounce(func, wait, debounceOptions);
+    }
+
+    /**
+     * Creates a function that provides `value` to the wrapper function as its
+     * first argument. Additional arguments provided to the function are appended
+     * to those provided to the wrapper function. The wrapper is executed with
+     * the `this` binding of the created function.
+     *
+     * @static
+     * @memberOf _
+     * @category Functions
+     * @param {*} value The value to wrap.
+     * @param {Function} wrapper The wrapper function.
+     * @returns {Function} Returns the new function.
+     * @example
+     *
+     * var p = _.wrap(_.escape, function(func, text) {
+     *   return '<p>' + func(text) + '</p>';
+     * });
+     *
+     * p('Fred, Wilma, & Pebbles');
+     * // => '<p>Fred, Wilma, &amp; Pebbles</p>'
+     */
+    function wrap(value, wrapper) {
+      return createWrapper(wrapper, 16, [value]);
+    }
+
+    /*--------------------------------------------------------------------------*/
+
+    /**
+     * Creates a function that returns `value`.
+     *
+     * @static
+     * @memberOf _
+     * @category Utilities
+     * @param {*} value The value to return from the new function.
+     * @returns {Function} Returns the new function.
+     * @example
+     *
+     * var object = { 'name': 'fred' };
+     * var getter = _.constant(object);
+     * getter() === object;
+     * // => true
+     */
+    function constant(value) {
+      return function() {
+        return value;
+      };
+    }
+
+    /**
+     * Produces a callback bound to an optional `thisArg`. If `func` is a property
+     * name the created callback will return the property value for a given element.
+     * If `func` is an object the created callback will return `true` for elements
+     * that contain the equivalent object properties, otherwise it will return `false`.
+     *
+     * @static
+     * @memberOf _
+     * @category Utilities
+     * @param {*} [func=identity] The value to convert to a callback.
+     * @param {*} [thisArg] The `this` binding of the created callback.
+     * @param {number} [argCount] The number of arguments the callback accepts.
+     * @returns {Function} Returns a callback function.
+     * @example
+     *
+     * var characters = [
+     *   { 'name': 'barney', 'age': 36 },
+     *   { 'name': 'fred',   'age': 40 }
+     * ];
+     *
+     * // wrap to create custom callback shorthands
+     * _.createCallback = _.wrap(_.createCallback, function(func, callback, thisArg) {
+     *   var match = /^(.+?)__([gl]t)(.+)$/.exec(callback);
+     *   return !match ? func(callback, thisArg) : function(object) {
+     *     return match[2] == 'gt' ? object[match[1]] > match[3] : object[match[1]] < match[3];
+     *   };
+     * });
+     *
+     * _.filter(characters, 'age__gt38');
+     * // => [{ 'name': 'fred', 'age': 40 }]
+     */
+    function createCallback(func, thisArg, argCount) {
+      var type = typeof func;
+      if (func == null || type == 'function') {
+        return baseCreateCallback(func, thisArg, argCount);
+      }
+      // handle "_.pluck" style callback shorthands
+      if (type != 'object') {
+        return property(func);
+      }
+      var props = keys(func),
+          key = props[0],
+          a = func[key];
+
+      // handle "_.where" style callback shorthands
+      if (props.length == 1 && a === a && !isObject(a)) {
+        // fast path the common case of providing an object with a single
+        // property containing a primitive value
+        return function(object) {
+          var b = object[key];
+          return a === b && (a !== 0 || (1 / a == 1 / b));
+        };
+      }
+      return function(object) {
+        var length = props.length,
+            result = false;
+
+        while (length--) {
+          if (!(result = baseIsEqual(object[props[length]], func[props[length]], null, true))) {
+            break;
+          }
+        }
+        return result;
+      };
+    }
+
+    /**
+     * Converts the characters `&`, `<`, `>`, `"`, and `'` in `string` to their
+     * corresponding HTML entities.
+     *
+     * @static
+     * @memberOf _
+     * @category Utilities
+     * @param {string} string The string to escape.
+     * @returns {string} Returns the escaped string.
+     * @example
+     *
+     * _.escape('Fred, Wilma, & Pebbles');
+     * // => 'Fred, Wilma, &amp; Pebbles'
+     */
+    function escape(string) {
+      return string == null ? '' : String(string).replace(reUnescapedHtml, escapeHtmlChar);
+    }
+
+    /**
+     * This method returns the first argument provided to it.
+     *
+     * @static
+     * @memberOf _
+     * @category Utilities
+     * @param {*} value Any value.
+     * @returns {*} Returns `value`.
+     * @example
+     *
+     * var object = { 'name': 'fred' };
+     * _.identity(object) === object;
+     * // => true
+     */
+    function identity(value) {
+      return value;
+    }
+
+    /**
+     * Adds function properties of a source object to the destination object.
+     * If `object` is a function methods will be added to its prototype as well.
+     *
+     * @static
+     * @memberOf _
+     * @category Utilities
+     * @param {Function|Object} [object=lodash] object The destination object.
+     * @param {Object} source The object of functions to add.
+     * @param {Object} [options] The options object.
+     * @param {boolean} [options.chain=true] Specify whether the functions added are chainable.
+     * @example
+     *
+     * function capitalize(string) {
+     *   return string.charAt(0).toUpperCase() + string.slice(1).toLowerCase();
+     * }
+     *
+     * _.mixin({ 'capitalize': capitalize });
+     * _.capitalize('fred');
+     * // => 'Fred'
+     *
+     * _('fred').capitalize().value();
+     * // => 'Fred'
+     *
+     * _.mixin({ 'capitalize': capitalize }, { 'chain': false });
+     * _('fred').capitalize();
+     * // => 'Fred'
+     */
+    function mixin(object, source, options) {
+      var chain = true,
+          methodNames = source && functions(source);
+
+      if (!source || (!options && !methodNames.length)) {
+        if (options == null) {
+          options = source;
+        }
+        ctor = lodashWrapper;
+        source = object;
+        object = lodash;
+        methodNames = functions(source);
+      }
+      if (options === false) {
+        chain = false;
+      } else if (isObject(options) && 'chain' in options) {
+        chain = options.chain;
+      }
+      var ctor = object,
+          isFunc = isFunction(ctor);
+
+      forEach(methodNames, function(methodName) {
+        var func = object[methodName] = source[methodName];
+        if (isFunc) {
+          ctor.prototype[methodName] = function() {
+            var chainAll = this.__chain__,
+                value = this.__wrapped__,
+                args = [value];
+
+            push.apply(args, arguments);
+            var result = func.apply(object, args);
+            if (chain || chainAll) {
+              if (value === result && isObject(result)) {
+                return this;
+              }
+              result = new ctor(result);
+              result.__chain__ = chainAll;
+            }
+            return result;
+          };
+        }
+      });
+    }
+
+    /**
+     * Reverts the '_' variable to its previous value and returns a reference to
+     * the `lodash` function.
+     *
+     * @static
+     * @memberOf _
+     * @category Utilities
+     * @returns {Function} Returns the `lodash` function.
+     * @example
+     *
+     * var lodash = _.noConflict();
+     */
+    function noConflict() {
+      context._ = oldDash;
+      return this;
+    }
+
+    /**
+     * A no-operation function.
+     *
+     * @static
+     * @memberOf _
+     * @category Utilities
+     * @example
+     *
+     * var object = { 'name': 'fred' };
+     * _.noop(object) === undefined;
+     * // => true
+     */
+    function noop() {
+      // no operation performed
+    }
+
+    /**
+     * Gets the number of milliseconds that have elapsed since the Unix epoch
+     * (1 January 1970 00:00:00 UTC).
+     *
+     * @static
+     * @memberOf _
+     * @category Utilities
+     * @example
+     *
+     * var stamp = _.now();
+     * _.defer(function() { console.log(_.now() - stamp); });
+     * // => logs the number of milliseconds it took for the deferred function to be called
+     */
+    var now = isNative(now = Date.now) && now || function() {
+      return new Date().getTime();
+    };
+
+    /**
+     * Converts the given value into an integer of the specified radix.
+     * If `radix` is `undefined` or `0` a `radix` of `10` is used unless the
+     * `value` is a hexadecimal, in which case a `radix` of `16` is used.
+     *
+     * Note: This method avoids differences in native ES3 and ES5 `parseInt`
+     * implementations. See http://es5.github.io/#E.
+     *
+     * @static
+     * @memberOf _
+     * @category Utilities
+     * @param {string} value The value to parse.
+     * @param {number} [radix] The radix used to interpret the value to parse.
+     * @returns {number} Returns the new integer value.
+     * @example
+     *
+     * _.parseInt('08');
+     * // => 8
+     */
+    var parseInt = nativeParseInt(whitespace + '08') == 8 ? nativeParseInt : function(value, radix) {
+      // Firefox < 21 and Opera < 15 follow the ES3 specified implementation of `parseInt`
+      return nativeParseInt(isString(value) ? value.replace(reLeadingSpacesAndZeros, '') : value, radix || 0);
+    };
+
+    /**
+     * Creates a "_.pluck" style function, which returns the `key` value of a
+     * given object.
+     *
+     * @static
+     * @memberOf _
+     * @category Utilities
+     * @param {string} key The name of the property to retrieve.
+     * @returns {Function} Returns the new function.
+     * @example
+     *
+     * var characters = [
+     *   { 'name': 'fred',   'age': 40 },
+     *   { 'name': 'barney', 'age': 36 }
+     * ];
+     *
+     * var getName = _.property('name');
+     *
+     * _.map(characters, getName);
+     * // => ['barney', 'fred']
+     *
+     * _.sortBy(characters, getName);
+     * // => [{ 'name': 'barney', 'age': 36 }, { 'name': 'fred',   'age': 40 }]
+     */
+    function property(key) {
+      return function(object) {
+        return object[key];
+      };
+    }
+
+    /**
+     * Produces a random number between `min` and `max` (inclusive). If only one
+     * argument is provided a number between `0` and the given number will be
+     * returned. If `floating` is truey or either `min` or `max` are floats a
+     * floating-point number will be returned instead of an integer.
+     *
+     * @static
+     * @memberOf _
+     * @category Utilities
+     * @param {number} [min=0] The minimum possible value.
+     * @param {number} [max=1] The maximum possible value.
+     * @param {boolean} [floating=false] Specify returning a floating-point number.
+     * @returns {number} Returns a random number.
+     * @example
+     *
+     * _.random(0, 5);
+     * // => an integer between 0 and 5
+     *
+     * _.random(5);
+     * // => also an integer between 0 and 5
+     *
+     * _.random(5, true);
+     * // => a floating-point number between 0 and 5
+     *
+     * _.random(1.2, 5.2);
+     * // => a floating-point number between 1.2 and 5.2
+     */
+    function random(min, max, floating) {
+      var noMin = min == null,
+          noMax = max == null;
+
+      if (floating == null) {
+        if (typeof min == 'boolean' && noMax) {
+          floating = min;
+          min = 1;
+        }
+        else if (!noMax && typeof max == 'boolean') {
+          floating = max;
+          noMax = true;
+        }
+      }
+      if (noMin && noMax) {
+        max = 1;
+      }
+      min = +min || 0;
+      if (noMax) {
+        max = min;
+        min = 0;
+      } else {
+        max = +max || 0;
+      }
+      if (floating || min % 1 || max % 1) {
+        var rand = nativeRandom();
+        return nativeMin(min + (rand * (max - min + parseFloat('1e-' + ((rand +'').length - 1)))), max);
+      }
+      return baseRandom(min, max);
+    }
+
+    /**
+     * Resolves the value of property `key` on `object`. If `key` is a function
+     * it will be invoked with the `this` binding of `object` and its result returned,
+     * else the property value is returned. If `object` is falsey then `undefined`
+     * is returned.
+     *
+     * @static
+     * @memberOf _
+     * @category Utilities
+     * @param {Object} object The object to inspect.
+     * @param {string} key The name of the property to resolve.
+     * @returns {*} Returns the resolved value.
+     * @example
+     *
+     * var object = {
+     *   'cheese': 'crumpets',
+     *   'stuff': function() {
+     *     return 'nonsense';
+     *   }
+     * };
+     *
+     * _.result(object, 'cheese');
+     * // => 'crumpets'
+     *
+     * _.result(object, 'stuff');
+     * // => 'nonsense'
+     */
+    function result(object, key) {
+      if (object) {
+        var value = object[key];
+        return isFunction(value) ? object[key]() : value;
+      }
+    }
+
+    /**
+     * A micro-templating method that handles arbitrary delimiters, preserves
+     * whitespace, and correctly escapes quotes within interpolated code.
+     *
+     * Note: In the development build, `_.template` utilizes sourceURLs for easier
+     * debugging. See http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/#toc-sourceurl
+     *
+     * For more information on precompiling templates see:
+     * http://lodash.com/custom-builds
+     *
+     * For more information on Chrome extension sandboxes see:
+     * http://developer.chrome.com/stable/extensions/sandboxingEval.html
+     *
+     * @static
+     * @memberOf _
+     * @category Utilities
+     * @param {string} text The template text.
+     * @param {Object} data The data object used to populate the text.
+     * @param {Object} [options] The options object.
+     * @param {RegExp} [options.escape] The "escape" delimiter.
+     * @param {RegExp} [options.evaluate] The "evaluate" delimiter.
+     * @param {Object} [options.imports] An object to import into the template as local variables.
+     * @param {RegExp} [options.interpolate] The "interpolate" delimiter.
+     * @param {string} [sourceURL] The sourceURL of the template's compiled source.
+     * @param {string} [variable] The data object variable name.
+     * @returns {Function|string} Returns a compiled function when no `data` object
+     *  is given, else it returns the interpolated text.
+     * @example
+     *
+     * // using the "interpolate" delimiter to create a compiled template
+     * var compiled = _.template('hello <%= name %>');
+     * compiled({ 'name': 'fred' });
+     * // => 'hello fred'
+     *
+     * // using the "escape" delimiter to escape HTML in data property values
+     * _.template('<b><%- value %></b>', { 'value': '<script>' });
+     * // => '<b>&lt;script&gt;</b>'
+     *
+     * // using the "evaluate" delimiter to generate HTML
+     * var list = '<% _.forEach(people, function(name) { %><li><%- name %></li><% }); %>';
+     * _.template(list, { 'people': ['fred', 'barney'] });
+     * // => '<li>fred</li><li>barney</li>'
+     *
+     * // using the ES6 delimiter as an alternative to the default "interpolate" delimiter
+     * _.template('hello ${ name }', { 'name': 'pebbles' });
+     * // => 'hello pebbles'
+     *
+     * // using the internal `print` function in "evaluate" delimiters
+     * _.template('<% print("hello " + name); %>!', { 'name': 'barney' });
+     * // => 'hello barney!'
+     *
+     * // using a custom template delimiters
+     * _.templateSettings = {
+     *   'interpolate': /{{([\s\S]+?)}}/g
+     * };
+     *
+     * _.template('hello {{ name }}!', { 'name': 'mustache' });
+     * // => 'hello mustache!'
+     *
+     * // using the `imports` option to import jQuery
+     * var list = '<% jq.each(people, function(name) { %><li><%- name %></li><% }); %>';
+     * _.template(list, { 'people': ['fred', 'barney'] }, { 'imports': { 'jq': jQuery } });
+     * // => '<li>fred</li><li>barney</li>'
+     *
+     * // using the `sourceURL` option to specify a custom sourceURL for the template
+     * var compiled = _.template('hello <%= name %>', null, { 'sourceURL': '/basic/greeting.jst' });
+     * compiled(data);
+     * // => find the source of "greeting.jst" under the Sources tab or Resources panel of the web inspector
+     *
+     * // using the `variable` option to ensure a with-statement isn't used in the compiled template
+     * var compiled = _.template('hi <%= data.name %>!', null, { 'variable': 'data' });
+     * compiled.source;
+     * // => function(data) {
+     *   var __t, __p = '', __e = _.escape;
+     *   __p += 'hi ' + ((__t = ( data.name )) == null ? '' : __t) + '!';
+     *   return __p;
+     * }
+     *
+     * // using the `source` property to inline compiled templates for meaningful
+     * // line numbers in error messages and a stack trace
+     * fs.writeFileSync(path.join(cwd, 'jst.js'), '\
+     *   var JST = {\
+     *     "main": ' + _.template(mainText).source + '\
+     *   };\
+     * ');
+     */
+    function template(text, data, options) {
+      // based on John Resig's `tmpl` implementation
+      // http://ejohn.org/blog/javascript-micro-templating/
+      // and Laura Doktorova's doT.js
+      // https://github.com/olado/doT
+      var settings = lodash.templateSettings;
+      text = String(text || '');
+
+      // avoid missing dependencies when `iteratorTemplate` is not defined
+      options = defaults({}, options, settings);
+
+      var imports = defaults({}, options.imports, settings.imports),
+          importsKeys = keys(imports),
+          importsValues = values(imports);
+
+      var isEvaluating,
+          index = 0,
+          interpolate = options.interpolate || reNoMatch,
+          source = "__p += '";
+
+      // compile the regexp to match each delimiter
+      var reDelimiters = RegExp(
+        (options.escape || reNoMatch).source + '|' +
+        interpolate.source + '|' +
+        (interpolate === reInterpolate ? reEsTemplate : reNoMatch).source + '|' +
+        (options.evaluate || reNoMatch).source + '|$'
+      , 'g');
+
+      text.replace(reDelimiters, function(match, escapeValue, interpolateValue, esTemplateValue, evaluateValue, offset) {
+        interpolateValue || (interpolateValue = esTemplateValue);
+
+        // escape characters that cannot be included in string literals
+        source += text.slice(index, offset).replace(reUnescapedString, escapeStringChar);
+
+        // replace delimiters with snippets
+        if (escapeValue) {
+          source += "' +\n__e(" + escapeValue + ") +\n'";
+        }
+        if (evaluateValue) {
+          isEvaluating = true;
+          source += "';\n" + evaluateValue + ";\n__p += '";
+        }
+        if (interpolateValue) {
+          source += "' +\n((__t = (" + interpolateValue + ")) == null ? '' : __t) +\n'";
+        }
+        index = offset + match.length;
+
+        // the JS engine embedded in Adobe products requires returning the `match`
+        // string in order to produce the correct `offset` value
+        return match;
+      });
+
+      source += "';\n";
+
+      // if `variable` is not specified, wrap a with-statement around the generated
+      // code to add the data object to the top of the scope chain
+      var variable = options.variable,
+          hasVariable = variable;
+
+      if (!hasVariable) {
+        variable = 'obj';
+        source = 'with (' + variable + ') {\n' + source + '\n}\n';
+      }
+      // cleanup code by stripping empty strings
+      source = (isEvaluating ? source.replace(reEmptyStringLeading, '') : source)
+        .replace(reEmptyStringMiddle, '$1')
+        .replace(reEmptyStringTrailing, '$1;');
+
+      // frame code as the function body
+      source = 'function(' + variable + ') {\n' +
+        (hasVariable ? '' : variable + ' || (' + variable + ' = {});\n') +
+        "var __t, __p = '', __e = _.escape" +
+        (isEvaluating
+          ? ', __j = Array.prototype.join;\n' +
+            "function print() { __p += __j.call(arguments, '') }\n"
+          : ';\n'
+        ) +
+        source +
+        'return __p\n}';
+
+      // Use a sourceURL for easier debugging.
+      // http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/#toc-sourceurl
+      var sourceURL = '\n/*\n//# sourceURL=' + (options.sourceURL || '/lodash/template/source[' + (templateCounter++) + ']') + '\n*/';
+
+      try {
+        var result = Function(importsKeys, 'return ' + source + sourceURL).apply(undefined, importsValues);
+      } catch(e) {
+        e.source = source;
+        throw e;
+      }
+      if (data) {
+        return result(data);
+      }
+      // provide the compiled function's source by its `toString` method, in
+      // supported environments, or the `source` property as a convenience for
+      // inlining compiled templates during the build process
+      result.source = source;
+      return result;
+    }
+
+    /**
+     * Executes the callback `n` times, returning an array of the results
+     * of each callback execution. The callback is bound to `thisArg` and invoked
+     * with one argument; (index).
+     *
+     * @static
+     * @memberOf _
+     * @category Utilities
+     * @param {number} n The number of times to execute the callback.
+     * @param {Function} callback The function called per iteration.
+     * @param {*} [thisArg] The `this` binding of `callback`.
+     * @returns {Array} Returns an array of the results of each `callback` execution.
+     * @example
+     *
+     * var diceRolls = _.times(3, _.partial(_.random, 1, 6));
+     * // => [3, 6, 4]
+     *
+     * _.times(3, function(n) { mage.castSpell(n); });
+     * // => calls `mage.castSpell(n)` three times, passing `n` of `0`, `1`, and `2` respectively
+     *
+     * _.times(3, function(n) { this.cast(n); }, mage);
+     * // => also calls `mage.castSpell(n)` three times
+     */
+    function times(n, callback, thisArg) {
+      n = (n = +n) > -1 ? n : 0;
+      var index = -1,
+          result = Array(n);
+
+      callback = baseCreateCallback(callback, thisArg, 1);
+      while (++index < n) {
+        result[index] = callback(index);
+      }
+      return result;
+    }
+
+    /**
+     * The inverse of `_.escape` this method converts the HTML entities
+     * `&amp;`, `&lt;`, `&gt;`, `&quot;`, and `&#39;` in `string` to their
+     * corresponding characters.
+     *
+     * @static
+     * @memberOf _
+     * @category Utilities
+     * @param {string} string The string to unescape.
+     * @returns {string} Returns the unescaped string.
+     * @example
+     *
+     * _.unescape('Fred, Barney &amp; Pebbles');
+     * // => 'Fred, Barney & Pebbles'
+     */
+    function unescape(string) {
+      return string == null ? '' : String(string).replace(reEscapedHtml, unescapeHtmlChar);
+    }
+
+    /**
+     * Generates a unique ID. If `prefix` is provided the ID will be appended to it.
+     *
+     * @static
+     * @memberOf _
+     * @category Utilities
+     * @param {string} [prefix] The value to prefix the ID with.
+     * @returns {string} Returns the unique ID.
+     * @example
+     *
+     * _.uniqueId('contact_');
+     * // => 'contact_104'
+     *
+     * _.uniqueId();
+     * // => '105'
+     */
+    function uniqueId(prefix) {
+      var id = ++idCounter;
+      return String(prefix == null ? '' : prefix) + id;
+    }
+
+    /*--------------------------------------------------------------------------*/
+
+    /**
+     * Creates a `lodash` object that wraps the given value with explicit
+     * method chaining enabled.
+     *
+     * @static
+     * @memberOf _
+     * @category Chaining
+     * @param {*} value The value to wrap.
+     * @returns {Object} Returns the wrapper object.
+     * @example
+     *
+     * var characters = [
+     *   { 'name': 'barney',  'age': 36 },
+     *   { 'name': 'fred',    'age': 40 },
+     *   { 'name': 'pebbles', 'age': 1 }
+     * ];
+     *
+     * var youngest = _.chain(characters)
+     *     .sortBy('age')
+     *     .map(function(chr) { return chr.name + ' is ' + chr.age; })
+     *     .first()
+     *     .value();
+     * // => 'pebbles is 1'
+     */
+    function chain(value) {
+      value = new lodashWrapper(value);
+      value.__chain__ = true;
+      return value;
+    }
+
+    /**
+     * Invokes `interceptor` with the `value` as the first argument and then
+     * returns `value`. The purpose of this method is to "tap into" a method
+     * chain in order to perform operations on intermediate results within
+     * the chain.
+     *
+     * @static
+     * @memberOf _
+     * @category Chaining
+     * @param {*} value The value to provide to `interceptor`.
+     * @param {Function} interceptor The function to invoke.
+     * @returns {*} Returns `value`.
+     * @example
+     *
+     * _([1, 2, 3, 4])
+     *  .tap(function(array) { array.pop(); })
+     *  .reverse()
+     *  .value();
+     * // => [3, 2, 1]
+     */
+    function tap(value, interceptor) {
+      interceptor(value);
+      return value;
+    }
+
+    /**
+     * Enables explicit method chaining on the wrapper object.
+     *
+     * @name chain
+     * @memberOf _
+     * @category Chaining
+     * @returns {*} Returns the wrapper object.
+     * @example
+     *
+     * var characters = [
+     *   { 'name': 'barney', 'age': 36 },
+     *   { 'name': 'fred',   'age': 40 }
+     * ];
+     *
+     * // without explicit chaining
+     * _(characters).first();
+     * // => { 'name': 'barney', 'age': 36 }
+     *
+     * // with explicit chaining
+     * _(characters).chain()
+     *   .first()
+     *   .pick('age')
+     *   .value();
+     * // => { 'age': 36 }
+     */
+    function wrapperChain() {
+      this.__chain__ = true;
+      return this;
+    }
+
+    /**
+     * Produces the `toString` result of the wrapped value.
+     *
+     * @name toString
+     * @memberOf _
+     * @category Chaining
+     * @returns {string} Returns the string result.
+     * @example
+     *
+     * _([1, 2, 3]).toString();
+     * // => '1,2,3'
+     */
+    function wrapperToString() {
+      return String(this.__wrapped__);
+    }
+
+    /**
+     * Extracts the wrapped value.
+     *
+     * @name valueOf
+     * @memberOf _
+     * @alias value
+     * @category Chaining
+     * @returns {*} Returns the wrapped value.
+     * @example
+     *
+     * _([1, 2, 3]).valueOf();
+     * // => [1, 2, 3]
+     */
+    function wrapperValueOf() {
+      return this.__wrapped__;
+    }
+
+    /*--------------------------------------------------------------------------*/
+
+    // add functions that return wrapped values when chaining
+    lodash.after = after;
+    lodash.assign = assign;
+    lodash.at = at;
+    lodash.bind = bind;
+    lodash.bindAll = bindAll;
+    lodash.bindKey = bindKey;
+    lodash.chain = chain;
+    lodash.compact = compact;
+    lodash.compose = compose;
+    lodash.constant = constant;
+    lodash.countBy = countBy;
+    lodash.create = create;
+    lodash.createCallback = createCallback;
+    lodash.curry = curry;
+    lodash.debounce = debounce;
+    lodash.defaults = defaults;
+    lodash.defer = defer;
+    lodash.delay = delay;
+    lodash.difference = difference;
+    lodash.filter = filter;
+    lodash.flatten = flatten;
+    lodash.forEach = forEach;
+    lodash.forEachRight = forEachRight;
+    lodash.forIn = forIn;
+    lodash.forInRight = forInRight;
+    lodash.forOwn = forOwn;
+    lodash.forOwnRight = forOwnRight;
+    lodash.functions = functions;
+    lodash.groupBy = groupBy;
+    lodash.indexBy = indexBy;
+    lodash.initial = initial;
+    lodash.intersection = intersection;
+    lodash.invert = invert;
+    lodash.invoke = invoke;
+    lodash.keys = keys;
+    lodash.map = map;
+    lodash.mapValues = mapValues;
+    lodash.max = max;
+    lodash.memoize = memoize;
+    lodash.merge = merge;
+    lodash.min = min;
+    lodash.omit = omit;
+    lodash.once = once;
+    lodash.pairs = pairs;
+    lodash.partial = partial;
+    lodash.partialRight = partialRight;
+    lodash.pick = pick;
+    lodash.pluck = pluck;
+    lodash.property = property;
+    lodash.pull = pull;
+    lodash.range = range;
+    lodash.reject = reject;
+    lodash.remove = remove;
+    lodash.rest = rest;
+    lodash.shuffle = shuffle;
+    lodash.sortBy = sortBy;
+    lodash.tap = tap;
+    lodash.throttle = throttle;
+    lodash.times = times;
+    lodash.toArray = toArray;
+    lodash.transform = transform;
+    lodash.union = union;
+    lodash.uniq = uniq;
+    lodash.values = values;
+    lodash.where = where;
+    lodash.without = without;
+    lodash.wrap = wrap;
+    lodash.xor = xor;
+    lodash.zip = zip;
+    lodash.zipObject = zipObject;
+
+    // add aliases
+    lodash.collect = map;
+    lodash.drop = rest;
+    lodash.each = forEach;
+    lodash.eachRight = forEachRight;
+    lodash.extend = assign;
+    lodash.methods = functions;
+    lodash.object = zipObject;
+    lodash.select = filter;
+    lodash.tail = rest;
+    lodash.unique = uniq;
+    lodash.unzip = zip;
+
+    // add functions to `lodash.prototype`
+    mixin(lodash);
+
+    /*--------------------------------------------------------------------------*/
+
+    // add functions that return unwrapped values when chaining
+    lodash.clone = clone;
+    lodash.cloneDeep = cloneDeep;
+    lodash.contains = contains;
+    lodash.escape = escape;
+    lodash.every = every;
+    lodash.find = find;
+    lodash.findIndex = findIndex;
+    lodash.findKey = findKey;
+    lodash.findLast = findLast;
+    lodash.findLastIndex = findLastIndex;
+    lodash.findLastKey = findLastKey;
+    lodash.has = has;
+    lodash.identity = identity;
+    lodash.indexOf = indexOf;
+    lodash.isArguments = isArguments;
+    lodash.isArray = isArray;
+    lodash.isBoolean = isBoolean;
+    lodash.isDate = isDate;
+    lodash.isElement = isElement;
+    lodash.isEmpty = isEmpty;
+    lodash.isEqual = isEqual;
+    lodash.isFinite = isFinite;
+    lodash.isFunction = isFunction;
+    lodash.isNaN = isNaN;
+    lodash.isNull = isNull;
+    lodash.isNumber = isNumber;
+    lodash.isObject = isObject;
+    lodash.isPlainObject = isPlainObject;
+    lodash.isRegExp = isRegExp;
+    lodash.isString = isString;
+    lodash.isUndefined = isUndefined;
+    lodash.lastIndexOf = lastIndexOf;
+    lodash.mixin = mixin;
+    lodash.noConflict = noConflict;
+    lodash.noop = noop;
+    lodash.now = now;
+    lodash.parseInt = parseInt;
+    lodash.random = random;
+    lodash.reduce = reduce;
+    lodash.reduceRight = reduceRight;
+    lodash.result = result;
+    lodash.runInContext = runInContext;
+    lodash.size = size;
+    lodash.some = some;
+    lodash.sortedIndex = sortedIndex;
+    lodash.template = template;
+    lodash.unescape = unescape;
+    lodash.uniqueId = uniqueId;
+
+    // add aliases
+    lodash.all = every;
+    lodash.any = some;
+    lodash.detect = find;
+    lodash.findWhere = find;
+    lodash.foldl = reduce;
+    lodash.foldr = reduceRight;
+    lodash.include = contains;
+    lodash.inject = reduce;
+
+    mixin(function() {
+      var source = {}
+      forOwn(lodash, function(func, methodName) {
+        if (!lodash.prototype[methodName]) {
+          source[methodName] = func;
+        }
+      });
+      return source;
+    }(), false);
+
+    /*--------------------------------------------------------------------------*/
+
+    // add functions capable of returning wrapped and unwrapped values when chaining
+    lodash.first = first;
+    lodash.last = last;
+    lodash.sample = sample;
+
+    // add aliases
+    lodash.take = first;
+    lodash.head = first;
+
+    forOwn(lodash, function(func, methodName) {
+      var callbackable = methodName !== 'sample';
+      if (!lodash.prototype[methodName]) {
+        lodash.prototype[methodName]= function(n, guard) {
+          var chainAll = this.__chain__,
+              result = func(this.__wrapped__, n, guard);
+
+          return !chainAll && (n == null || (guard && !(callbackable && typeof n == 'function')))
+            ? result
+            : new lodashWrapper(result, chainAll);
+        };
+      }
+    });
+
+    /*--------------------------------------------------------------------------*/
+
+    /**
+     * The semantic version number.
+     *
+     * @static
+     * @memberOf _
+     * @type string
+     */
+    lodash.VERSION = '2.4.1';
+
+    // add "Chaining" functions to the wrapper
+    lodash.prototype.chain = wrapperChain;
+    lodash.prototype.toString = wrapperToString;
+    lodash.prototype.value = wrapperValueOf;
+    lodash.prototype.valueOf = wrapperValueOf;
+
+    // add `Array` functions that return unwrapped values
+    forEach(['join', 'pop', 'shift'], function(methodName) {
+      var func = arrayRef[methodName];
+      lodash.prototype[methodName] = function() {
+        var chainAll = this.__chain__,
+            result = func.apply(this.__wrapped__, arguments);
+
+        return chainAll
+          ? new lodashWrapper(result, chainAll)
+          : result;
+      };
+    });
+
+    // add `Array` functions that return the existing wrapped value
+    forEach(['push', 'reverse', 'sort', 'unshift'], function(methodName) {
+      var func = arrayRef[methodName];
+      lodash.prototype[methodName] = function() {
+        func.apply(this.__wrapped__, arguments);
+        return this;
+      };
+    });
+
+    // add `Array` functions that return new wrapped values
+    forEach(['concat', 'slice', 'splice'], function(methodName) {
+      var func = arrayRef[methodName];
+      lodash.prototype[methodName] = function() {
+        return new lodashWrapper(func.apply(this.__wrapped__, arguments), this.__chain__);
+      };
+    });
+
+    return lodash;
+  }
+
+  /*--------------------------------------------------------------------------*/
+
+  // expose Lo-Dash
+  var _ = runInContext();
+
+  // some AMD build optimizers like r.js check for condition patterns like the following:
+  if (typeof define == 'function' && typeof define.amd == 'object' && define.amd) {
+    // Expose Lo-Dash to the global object even when an AMD loader is present in
+    // case Lo-Dash is loaded with a RequireJS shim config.
+    // See http://requirejs.org/docs/api.html#config-shim
+    root._ = _;
+
+    // define as an anonymous module so, through path mapping, it can be
+    // referenced as the "underscore" module
+    define(function() {
+      return _;
+    });
+  }
+  // check for `exports` after `define` in case a build optimizer adds an `exports` object
+  else if (freeExports && freeModule) {
+    // in Node.js or RingoJS
+    if (moduleExports) {
+      (freeModule.exports = _)._ = _;
+    }
+    // in Narwhal or Rhino -require
+    else {
+      freeExports._ = _;
+    }
+  }
+  else {
+    // in a browser or Rhino
+    root._ = _;
+  }
+}.call(this));
diff --git a/packages/context-coloring/benchmark/fixtures/mkdirp-0.5.0.js b/packages/context-coloring/benchmark/fixtures/mkdirp-0.5.0.js
new file mode 100644
index 0000000..a1742b2
--- /dev/null
+++ b/packages/context-coloring/benchmark/fixtures/mkdirp-0.5.0.js
@@ -0,0 +1,97 @@
+var path = require('path');
+var fs = require('fs');
+
+module.exports = mkdirP.mkdirp = mkdirP.mkdirP = mkdirP;
+
+function mkdirP (p, opts, f, made) {
+    if (typeof opts === 'function') {
+        f = opts;
+        opts = {};
+    }
+    else if (!opts || typeof opts !== 'object') {
+        opts = { mode: opts };
+    }
+    
+    var mode = opts.mode;
+    var xfs = opts.fs || fs;
+    
+    if (mode === undefined) {
+        mode = 0777 & (~process.umask());
+    }
+    if (!made) made = null;
+    
+    var cb = f || function () {};
+    p = path.resolve(p);
+    
+    xfs.mkdir(p, mode, function (er) {
+        if (!er) {
+            made = made || p;
+            return cb(null, made);
+        }
+        switch (er.code) {
+            case 'ENOENT':
+                mkdirP(path.dirname(p), opts, function (er, made) {
+                    if (er) cb(er, made);
+                    else mkdirP(p, opts, cb, made);
+                });
+                break;
+
+            // In the case of any other error, just see if there's a dir
+            // there already.  If so, then hooray!  If not, then something
+            // is borked.
+            default:
+                xfs.stat(p, function (er2, stat) {
+                    // if the stat fails, then that's super weird.
+                    // let the original error be the failure reason.
+                    if (er2 || !stat.isDirectory()) cb(er, made)
+                    else cb(null, made);
+                });
+                break;
+        }
+    });
+}
+
+mkdirP.sync = function sync (p, opts, made) {
+    if (!opts || typeof opts !== 'object') {
+        opts = { mode: opts };
+    }
+    
+    var mode = opts.mode;
+    var xfs = opts.fs || fs;
+    
+    if (mode === undefined) {
+        mode = 0777 & (~process.umask());
+    }
+    if (!made) made = null;
+
+    p = path.resolve(p);
+
+    try {
+        xfs.mkdirSync(p, mode);
+        made = made || p;
+    }
+    catch (err0) {
+        switch (err0.code) {
+            case 'ENOENT' :
+                made = sync(path.dirname(p), opts, made);
+                sync(p, opts, made);
+                break;
+
+            // In the case of any other error, just see if there's a dir
+            // there already.  If so, then hooray!  If not, then something
+            // is borked.
+            default:
+                var stat;
+                try {
+                    stat = xfs.statSync(p);
+                }
+                catch (err1) {
+                    throw err0;
+                }
+                if (!stat.isDirectory()) throw err0;
+                break;
+        }
+    }
+
+    return made;
+};
diff --git a/packages/context-coloring/context-coloring.el b/packages/context-coloring/context-coloring.el
new file mode 100644
index 0000000..5a33eff
--- /dev/null
+++ b/packages/context-coloring/context-coloring.el
@@ -0,0 +1,607 @@
+;;; context-coloring.el --- Syntax highlighting, except not for syntax. -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2014-2015  Free Software Foundation, Inc.
+
+;; Author: Jackson Ray Hamilton <jackson@jacksonrayhamilton.com>
+;; URL: https://github.com/jacksonrayhamilton/context-coloring
+;; Keywords: context coloring syntax highlighting
+;; Version: 4.0.0
+;; Package-Requires: ((emacs "24") (js2-mode "20150126"))
+
+;; This file is part of GNU Emacs.
+
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; Highlights code according to function context.
+
+;; - Code in the global scope is one color.  Code in functions within the global
+;;   scope is a different color, and code within such functions is another
+;;   color, and so on.
+;; - Identifiers retain the color of the scope in which they are declared.
+
+;; Lexical scope information at-a-glance can assist a programmer in
+;; understanding the overall structure of a program.  It can help to curb nasty
+;; bugs like name shadowing.  A rainbow can indicate excessive complexity. State
+;; change within a closure is easily monitored.
+
+;; By default, Context Coloring still highlights comments and strings
+;; syntactically.  It is still easy to differentiate code from non-code, and
+;; strings cannot be confused for variables.
+
+;; To use, add the following to your ~/.emacs:
+
+;; (require 'context-coloring)
+;; (add-hook 'js2-mode-hook 'context-coloring-mode)
+
+;; js-mode or js3-mode support requires Node.js 0.10+ and the scopifier
+;; executable.
+
+;; $ npm install -g scopifier
+
+;;; Code:
+
+(require 'js2-mode)
+
+
+;;; Constants
+
+(defconst context-coloring-path
+  (file-name-directory (or load-file-name buffer-file-name))
+  "This file's directory.")
+
+
+;;; Customizable options
+
+(defcustom context-coloring-delay 0.25
+  "Delay between a buffer update and colorization.
+
+Increase this if your machine is high-performing.  Decrease it if
+it ain't.
+
+Supported modes: `js-mode', `js3-mode'"
+  :group 'context-coloring)
+
+(defcustom context-coloring-comments-and-strings t
+  "If non-nil, also color comments and strings using `font-lock'."
+  :group 'context-coloring)
+
+(defcustom context-coloring-js-block-scopes nil
+  "If non-nil, also color block scopes in the scope hierarchy in JavaScript.
+
+The block-scope-inducing `let' and `const' are introduced in ES6.
+If you are writing ES6 code, enable this; otherwise, don't.
+
+Supported modes: `js2-mode'"
+  :group 'context-coloring)
+
+(defcustom context-coloring-benchmark-colorization nil
+  "If non-nil, track how long colorization takes and print
+messages with the colorization duration."
+  :group 'context-coloring)
+
+
+;;; Local variables
+
+(defvar-local context-coloring-buffer nil
+  "Reference to this buffer (for timers).")
+
+(defvar-local context-coloring-scopifier-process nil
+  "Reference to the single scopifier process that can be
+  running.")
+
+(defvar-local context-coloring-colorize-idle-timer nil
+  "Reference to the currently-running idle timer.")
+
+(defvar-local context-coloring-changed nil
+  "Indication that the buffer has changed recently, which would
+imply that it should be colorized again by
+`context-coloring-colorize-idle-timer' if that timer is being
+used.")
+
+
+;;; Faces
+
+(defun context-coloring-defface (level tty light dark)
+  (let ((face (intern (format "context-coloring-level-%s-face" level)))
+        (doc (format "Context coloring face, level %s." level)))
+    (eval (macroexpand `(defface ,face
+                          '((((type tty)) (:foreground ,tty))
+                            (((background light)) (:foreground ,light))
+                            (((background dark)) (:foreground ,dark)))
+                          ,doc
+                          :group 'context-coloring)))))
+
+(defvar context-coloring-face-count nil
+  "Number of faces available for context coloring.")
+
+(defun context-coloring-defface-default (level)
+  (context-coloring-defface level "white" "#3f3f3f" "#cdcdcd"))
+
+(defun context-coloring-set-colors-default ()
+  (context-coloring-defface 0 "white"   "#000000" "#ffffff")
+  (context-coloring-defface 1 "yellow"  "#007f80" "#ffff80")
+  (context-coloring-defface 2 "green"   "#001580" "#cdfacd")
+  (context-coloring-defface 3 "cyan"    "#550080" "#d8d8ff")
+  (context-coloring-defface 4 "blue"    "#802b00" "#e7c7ff")
+  (context-coloring-defface 5 "magenta" "#6a8000" "#ffcdcd")
+  (context-coloring-defface 6 "red"     "#008000" "#ffe390")
+  (context-coloring-defface-default 7)
+  (setq context-coloring-face-count 8))
+
+(context-coloring-set-colors-default)
+
+
+;;; Face functions
+
+(defsubst context-coloring-face-symbol (level)
+  "Returns a symbol for a face with LEVEL."
+  ;; `concat' is faster than `format' here.
+  (intern-soft (concat "context-coloring-level-"
+                       (number-to-string level)
+                       "-face")))
+
+(defun context-coloring-set-colors (&rest colors)
+  "Set context coloring's levels' coloring to COLORS, where the
+Nth element of COLORS is level N's color."
+  (setq context-coloring-face-count (length colors))
+  (let ((level 0))
+    (dolist (color colors)
+      ;; Ensure there are available faces to contain new colors.
+      (when (not (context-coloring-face-symbol level))
+        (context-coloring-defface-default level))
+      (set-face-foreground (context-coloring-face-symbol level) color)
+      (setq level (+ level 1)))))
+
+(defsubst context-coloring-level-face (level)
+  "Returns the face name for LEVEL."
+  (context-coloring-face-symbol (min level context-coloring-face-count)))
+
+
+;;; Colorization utilities
+
+(defsubst context-coloring-colorize-region (start end level)
+  "Colorizes characters from the 1-indexed START (inclusive) to
+END (exclusive) with the face corresponding to LEVEL."
+  (add-text-properties
+   start
+   end
+   `(face ,(context-coloring-level-face level))))
+
+(defsubst context-coloring-maybe-colorize-comments-and-strings ()
+  "Colorizes the current buffer's comments and strings if
+`context-coloring-comments-and-strings' is non-nil."
+  (when context-coloring-comments-and-strings
+    (save-excursion
+      (font-lock-fontify-syntactically-region (point-min) (point-max)))))
+
+
+;;; js2-mode colorization
+
+(defvar-local context-coloring-js2-scope-level-hash-table nil
+  "Associates `js2-scope' structures and with their scope
+  levels.")
+
+(defsubst context-coloring-js2-scope-level (scope)
+  "Gets the level of SCOPE."
+  (cond ((gethash scope context-coloring-js2-scope-level-hash-table))
+        (t
+         (let ((level 0)
+               (current-scope scope)
+               enclosing-scope)
+           (while (and current-scope
+                       (js2-node-parent current-scope)
+                       (setq enclosing-scope
+                             (js2-node-get-enclosing-scope current-scope)))
+             (when (or context-coloring-js-block-scopes
+                       (let ((type (js2-scope-type current-scope)))
+                         (or (= type js2-SCRIPT)
+                             (= type js2-FUNCTION)
+                             (= type js2-CATCH))))
+               (setq level (+ level 1)))
+             (setq current-scope enclosing-scope))
+           (puthash scope level context-coloring-js2-scope-level-hash-table)))))
+
+(defsubst context-coloring-js2-local-name-node-p (node)
+  "Determines if NODE is a js2-name-node representing a local
+variable."
+  (and (js2-name-node-p node)
+       (let ((parent (js2-node-parent node)))
+         (not (or (and (js2-object-prop-node-p parent)
+                       (eq node (js2-object-prop-node-left parent)))
+                  (and (js2-prop-get-node-p parent)
+                       ;; For nested property lookup, the node on the left is a
+                       ;; `js2-prop-get-node', so this always works.
+                       (eq node (js2-prop-get-node-right parent))))))))
+
+(defsubst context-coloring-js2-colorize-node (node level)
+  "Colors NODE with the color for LEVEL."
+  (let ((start (js2-node-abs-pos node)))
+    (context-coloring-colorize-region
+     start
+     (+ start (js2-node-len node)) ; End
+     level)))
+
+(defun context-coloring-js2-colorize ()
+  "Colorizes the current buffer using the abstract syntax tree
+generated by js2-mode."
+  ;; Reset the hash table; the old one could be obsolete.
+  (setq context-coloring-js2-scope-level-hash-table (make-hash-table :test 'eq))
+  (with-silent-modifications
+    (js2-visit-ast
+     js2-mode-ast
+     (lambda (node end-p)
+       (when (null end-p)
+         (cond
+          ((js2-scope-p node)
+           (context-coloring-js2-colorize-node
+            node
+            (context-coloring-js2-scope-level node)))
+          ((context-coloring-js2-local-name-node-p node)
+           (let* ((enclosing-scope (js2-node-get-enclosing-scope node))
+                  (defining-scope (js2-get-defining-scope
+                                   enclosing-scope
+                                   (js2-name-node-name node))))
+             ;; The tree seems to be walked lexically, so an entire scope will
+             ;; be colored, including its name nodes, before they are reached.
+             ;; Coloring the nodes defined in that scope would be redundant, so
+             ;; don't do it.
+             (when (not (eq defining-scope enclosing-scope))
+               (context-coloring-js2-colorize-node
+                node
+                (context-coloring-js2-scope-level defining-scope))))))
+         ;; The `t' indicates to search children.
+         t)))
+    (context-coloring-maybe-colorize-comments-and-strings)))
+
+
+;;; Shell command scopification / colorization
+
+(defun context-coloring-apply-tokens (tokens)
+  "Processes a vector of TOKENS to apply context-based coloring
+to the current buffer.  Tokens are 3 integers: start, end, level.
+The vector is flat, with a new token occurring after every 3rd
+element."
+  (with-silent-modifications
+    (let ((i 0)
+          (len (length tokens)))
+      (while (< i len)
+        (context-coloring-colorize-region
+         (elt tokens i)
+         (elt tokens (+ i 1))
+         (elt tokens (+ i 2)))
+        (setq i (+ i 3))))
+    (context-coloring-maybe-colorize-comments-and-strings)))
+
+(defun context-coloring-parse-array (input)
+  "Specialized JSON parser for a flat array of numbers."
+  (vconcat (mapcar 'string-to-number (split-string (substring input 1 -1) ","))))
+
+(defun context-coloring-kill-scopifier ()
+  "Kills the currently-running scopifier process for this
+buffer."
+  (when (not (null context-coloring-scopifier-process))
+    (delete-process context-coloring-scopifier-process)
+    (setq context-coloring-scopifier-process nil)))
+
+(defun context-coloring-scopify-shell-command (command &optional callback)
+  "Invokes a scopifier with the current buffer's contents,
+reading the scopifier's response asynchronously and applying a
+parsed list of tokens to `context-coloring-apply-tokens'.
+
+Invokes CALLBACK when complete."
+
+  ;; Prior running tokenization is implicitly obsolete if this function is
+  ;; called.
+  (context-coloring-kill-scopifier)
+
+  ;; Start the process.
+  (setq context-coloring-scopifier-process
+        (start-process-shell-command "scopifier" nil command))
+
+  (let ((output "")
+        (buffer context-coloring-buffer))
+
+    ;; The process may produce output in multiple chunks.  This filter
+    ;; accumulates the chunks into a message.
+    (set-process-filter
+     context-coloring-scopifier-process
+     (lambda (_process chunk)
+       (setq output (concat output chunk))))
+
+    ;; When the process's message is complete, this sentinel parses it as JSON
+    ;; and applies the tokens to the buffer.
+    (set-process-sentinel
+     context-coloring-scopifier-process
+     (lambda (_process event)
+       (when (equal "finished\n" event)
+         (let ((tokens (context-coloring-parse-array output)))
+           (with-current-buffer buffer
+             (context-coloring-apply-tokens tokens))
+           (setq context-coloring-scopifier-process nil)
+           (if callback (funcall callback)))))))
+
+  ;; Give the process its input so it can begin.
+  (process-send-region context-coloring-scopifier-process (point-min) (point-max))
+  (process-send-eof context-coloring-scopifier-process))
+
+
+;;; Dispatch
+
+(defvar context-coloring-dispatch-hash-table (make-hash-table :test 'eq)
+  "Mapping of dispatch strategy names to their corresponding
+  property lists, which contain details about the strategies.")
+
+(defvar context-coloring-mode-hash-table (make-hash-table :test 'eq)
+  "Mapping of major mode names to dispatch property lists.")
+
+(defun context-coloring-select-dispatch (mode dispatch)
+  "Use DISPATCH for MODE."
+  (puthash
+   mode
+   (gethash
+    dispatch
+    context-coloring-dispatch-hash-table)
+   context-coloring-mode-hash-table))
+
+(defun context-coloring-define-dispatch (symbol &rest properties)
+  "Define a new dispatch named SYMBOL with PROPERTIES.
+
+A \"dispatch\" is a property list describing a strategy for
+coloring a buffer.  There are three possible strategies: Parse
+and color in a single function (`:colorizer'), parse in a
+function that returns scope data (`:scopifier'), or parse with a
+shell command that returns scope data (`:command').  In the
+latter two cases, the scope data will be used to automatically
+color the buffer.
+
+PROPERTIES must include `:modes' and one of `:colorizer',
+`:scopifier' or `:command'.
+
+`:modes' - List of major modes this dispatch is valid for.
+
+`:colorizer' - Symbol referring to a function that parses and
+colors the buffer.
+
+`:scopifier' - Symbol referring to a function that parses the
+buffer a returns a flat vector of start, end and level data.
+
+`:executable' - Optional name of an executable required by
+`:command'.
+
+`:command' - Shell command to execute with the current buffer
+sent via stdin, and with a flat JSON array of start, end and
+level data returned via stdout."
+  (let ((modes (plist-get properties :modes))
+        (colorizer (plist-get properties :colorizer))
+        (scopifier (plist-get properties :scopifier))
+        (command (plist-get properties :command)))
+    (when (null modes)
+      (error "No mode defined for dispatch"))
+    (when (not (or colorizer
+                   scopifier
+                   command))
+      (error "No colorizer, scopifier or command defined for dispatch"))
+    (puthash symbol properties context-coloring-dispatch-hash-table)
+    (dolist (mode modes)
+      (when (null (gethash mode context-coloring-mode-hash-table))
+        (puthash mode properties context-coloring-mode-hash-table)))))
+
+(context-coloring-define-dispatch
+ 'javascript-node
+ :modes '(js-mode js3-mode)
+ :executable "scopifier"
+ :command "scopifier")
+
+(context-coloring-define-dispatch
+ 'javascript-js2
+ :modes '(js2-mode)
+ :colorizer 'context-coloring-js2-colorize)
+
+(defun context-coloring-dispatch (&optional callback)
+  "Determines the optimal track for scopification / colorization
+of the current buffer, then executes it.
+
+Invokes CALLBACK when complete.  It is invoked synchronously for
+elisp tracks, and asynchronously for shell command tracks."
+  (let ((dispatch (gethash major-mode context-coloring-mode-hash-table)))
+    (if (null dispatch)
+        (message "%s" "Context coloring is not available for this major mode"))
+    (let (colorizer
+          scopifier
+          command
+          executable)
+      (cond
+       ((setq colorizer (plist-get dispatch :colorizer))
+        (funcall colorizer)
+        (if callback (funcall callback)))
+       ((setq scopifier (plist-get dispatch :scopifier))
+        (context-coloring-apply-tokens (funcall scopifier))
+        (if callback (funcall callback)))
+       ((setq command (plist-get dispatch :command))
+        (setq executable (plist-get dispatch :executable))
+        (if (and executable
+                 (null (executable-find executable)))
+            (message "Executable \"%s\" not found" executable)
+          (context-coloring-scopify-shell-command command callback)))))))
+
+
+;;; Colorization
+
+(defun context-coloring-colorize (&optional callback)
+  "Colors the current buffer by function context.
+
+Invokes CALLBACK when complete; see `context-coloring-dispatch'."
+  (interactive)
+  (let ((start-time (float-time)))
+    (context-coloring-dispatch
+     (lambda ()
+       (when context-coloring-benchmark-colorization
+         (message "Colorization took %.3f seconds" (- (float-time) start-time)))
+       (if callback (funcall callback))))))
+
+(defun context-coloring-change-function (_start _end _length)
+  "Registers a change so that a buffer can be colorized soon."
+  ;; Tokenization is obsolete if there was a change.
+  (context-coloring-kill-scopifier)
+  (setq context-coloring-changed t))
+
+(defun context-coloring-maybe-colorize ()
+  "Colorize unders certain conditions.  This will run as an idle
+timer, so firstly the buffer must not be some other buffer.
+Additionally, the buffer must have changed, otherwise colorizing
+would be redundant."
+  (when (and (eq context-coloring-buffer (window-buffer (selected-window)))
+             context-coloring-changed)
+    (setq context-coloring-changed nil)
+    (context-coloring-colorize)))
+
+
+;;; Themes
+
+(defvar context-coloring-theme-hash-table (make-hash-table :test 'eq)
+  "Mapping of theme names to theme properties.")
+
+(defun context-coloring-define-theme (theme &rest properties)
+  "Define a theme named THEME for coloring scope levels.
+PROPERTIES is a property list specifiying the following details:
+
+`:colors': List of colors that this theme uses."
+  (puthash
+   theme
+   (lambda ()
+     (apply 'context-coloring-set-colors (plist-get properties :colors)))
+   context-coloring-theme-hash-table))
+
+(defun context-coloring-load-theme (theme)
+  "Apply THEME's colors and other properties for context
+coloring."
+  (let ((function (gethash theme context-coloring-theme-hash-table)))
+    (when (null function)
+      (error (format "No such theme `%s'" theme)))
+    (funcall function)))
+
+(context-coloring-define-theme
+ 'monokai
+ :colors '("#F8F8F2"
+           "#66D9EF"
+           "#A1EFE4"
+           "#A6E22E"
+           "#E6DB74"
+           "#FD971F"
+           "#F92672"
+           "#FD5FF0"
+           "#AE81FF"))
+
+(context-coloring-define-theme
+ 'solarized
+ :colors '("#839496"
+           "#268bd2"
+           "#2aa198"
+           "#859900"
+           "#b58900"
+           "#cb4b16"
+           "#dc322f"
+           "#d33682"
+           "#6c71c4"
+           "#69B7F0"
+           "#69CABF"
+           "#B4C342"
+           "#DEB542"
+           "#F2804F"
+           "#FF6E64"
+           "#F771AC"
+           "#9EA0E5"))
+
+(context-coloring-define-theme
+ 'tango
+ :colors '("#2e3436"
+           "#346604"
+           "#204a87"
+           "#5c3566"
+           "#a40000"
+           "#b35000"
+           "#c4a000"
+           "#8ae234"
+           "#8cc4ff"
+           "#ad7fa8"
+           "#ef2929"
+           "#fcaf3e"
+           "#fce94f"))
+
+(context-coloring-define-theme
+ 'zenburn
+ :colors '("#DCDCCC"
+           "#93E0E3"
+           "#BFEBBF"
+           "#F0DFAF"
+           "#DFAF8F"
+           "#CC9393"
+           "#DC8CC3"
+           "#94BFF3"
+           "#9FC59F"
+           "#D0BF8F"
+           "#DCA3A3"))
+
+
+;;; Minor mode
+
+;;;###autoload
+(define-minor-mode context-coloring-mode
+  "Context-based code coloring, inspired by Douglas Crockford."
+  nil " Context" nil
+  (if (not context-coloring-mode)
+      (progn
+        (context-coloring-kill-scopifier)
+        (when context-coloring-colorize-idle-timer
+          (cancel-timer context-coloring-colorize-idle-timer))
+        (remove-hook 'js2-post-parse-callbacks 'context-coloring-colorize t)
+        (remove-hook 'after-change-functions 'context-coloring-change-function t)
+        (font-lock-mode)
+        (jit-lock-mode t))
+
+    ;; Remember this buffer. This value should not be dynamically-bound.
+    (setq context-coloring-buffer (current-buffer))
+
+    ;; Font lock is incompatible with this mode; the converse is also true.
+    (font-lock-mode 0)
+    (jit-lock-mode nil)
+
+    ;; Colorize once initially.
+    (context-coloring-colorize)
+
+    (cond
+     ((equal major-mode 'js2-mode)
+      ;; Only recolor on reparse.
+      (add-hook 'js2-post-parse-callbacks 'context-coloring-colorize nil t))
+     (t
+      ;; Only recolor on change.
+      (add-hook 'after-change-functions 'context-coloring-change-function nil t)))
+
+    (when (not (equal major-mode 'js2-mode))
+      ;; Only recolor idly.
+      (setq context-coloring-colorize-idle-timer
+            (run-with-idle-timer
+             context-coloring-delay
+             t
+             'context-coloring-maybe-colorize)))))
+
+(provide 'context-coloring)
+
+;; Local Variables:
+;; eval: (when (fboundp 'rainbow-mode) (rainbow-mode 1))
+;; End:
+
+;;; context-coloring.el ends here
diff --git a/packages/context-coloring/scopifier.png b/packages/context-coloring/scopifier.png
new file mode 100644
index 0000000000000000000000000000000000000000..1ec5d104269d700ac1020ad42eb63aa53aa67be1
GIT binary patch
literal 2609
zcmb_eXIGO66GcE!M1lnnabYbBiwV65NPGyP7^xvadQ*xJ2rVjvPH3TpCP?T+dWm#I
zLJwU6S)@ZKt{@5o4WhEEFZ&<fd(O<g_nbL%zs!drnwuJ)6}T+G#l>|NaaYgcm{*R=
z;uOzu7aSu?b8&HV%#G3d$M*jP006*Xu$h?|7K=S|=FG2OdpZCB0fX5rDi{m_063s)
z2>?1eI)EMk=pIiWE5Oh~6XM#>k^`)xqmXH@0!_0SY*xL6!SO0(KmqLgW_f`{WwT01
z4XNREA%KC?D`(|F78Qjuhd>};2-umnoK)a~_cj14!7WOsgF1%TtaOML63{U+f&qpw
zV9-~?SP85O0jEwD2U8PZW`28pJ}I@{a@zJWL@P5G09Me>96pYyG9?z?F?&Kp-O5EO
zdKkz*R0IF~xoh3Hw9TTfuTV7}CaOE)bxa<mmb(Kma~C^PTT_^cp$=(f4enWIn?o@o
z<at%5u;=1osIa+?z~~I8-G=**_4NXw@(N&Z=ITb$^g<Goxwtr<J30=3kc%a!=-MTv
zrN=J4e*CV|B@}NCQM>1E4Y$+36EZTFu{h(=$e5h!PF$whlG?jw8Y}u6Hd$1CuNRij
zQ)6mV_TD6<wnVVULXsQsw`_ftOnuwau!{Ol0RfIRK`0o)jO=RQ=LFbjLL!!?rWeK&
zmPh91Ml8s~?To&~xvqf;n!bNyEtA^aTkB7)afmBS?kUKvFVMwhIzNkBZj1b@#+zR1
zHIRHi#2rP-GEazvr_W4ejnNH5yEDt%Eh1`X88su!ir5lz5HT4+OhjU1Y$Bs$8zN)N
zA_0$x@jQ&<W0#>YIN_<TmWGzzEhR-2CD;Cq(CIbL?#b5q5j>ObRNVWhqBW7$k~UYD
z(OwrvE!$=mmR1!QJ<WTTPiC*=cC_T~%|1&S%*v!^vW8O9XmNu*v7M#CF`-_RB=^Lp
zPOswZN3v|4Tx}F@!##|lUdD<hNqzn=izXQ440?`RSnL~GSV)jRvD|kc&9ce|p{j+5
z@R4op@`*V<dU2eEDT<3v^ephZ1w!p6w*c=Ig^Pb)7v@*j7dN@hc^eJk;^O-Yp$9_;
zE$rk!Mqh+o>d~in++H7tZd}$dz9DojsYyta-iHzvp>y#J=%Bp^czMvHLf#Fp|HF}#
zhx(QJJ#-fI@G$gNNE2(ZfECV4K9cmU=i=e%2VU?B<@r4P<K=wJIZvxXUW#TGe-izs
zCyz)SicvTw$IJUg*GyUkZ0zM2iT-s^rcA--E1%qnk~$)NM8p>n3qJVX=pjC`muL;>
zO}?6|7zgSTy)`MAnR==Fu^{X5e%f1F@|Na8KylF<soBhF--oz4THfq{-Ts<bD1{{Z
z9n0%YVUYTT*$u)V#?Xa1iRq?5znJhpS`P9n`5}P;*RcIVJ?FT;O2D=^KFAPU*q40E
zl&j0x;l(mz4t*AWk?X?H?Y7={R&$g45BuJyxRo?V*(K80HO?IRFkzWTNia0QN>03k
zgXC_<Q?B36HW}}`I~;ypMD2#UlKfjIi?Yj3_LPpM7o&v+LE=Q;WYY5G4ebY^uHN?c
z$w{iW<<Bcj^qfv7#$QvsDKzEs(_S~?_T4rmsJIi!(Ph=cE^h1rW*gD!#-@wSfa(0|
z-{V0cyMml2Zu=?`D>>VSDyRA3k)L2D_x3n6a>fs(P|weOII$wiM5V^s5`mB}er{{N
zCpHm2D}JX+OiSl(KeT3l<eKIBp@jOqsv4`y-!ub0A)pe~cG|BZ?23GxzWc4NTA$|3
zU}`U*fhgQ&w0>ln4c|d^bMIn#f!!KO&j|GX)O(re#4$BPg-nD*gRse+m&}1KnIj70
zM&JBqpeI3+)>t++ujwZs+Bh}cUs}NniZ~RwuvetzzaEYBiCP)7FC+axD1Ti2^X!Kb
z^*CycVJh?}z(lyBEJs@wCKHmT6M#538zk%!v@0TO+ivY|=RlK8DRj_YPd+I>!sfSO
zPEmd!zs7NMJQyuknJVc}D1Pb-@dcvkfI%yF-nFUa=j+7Si1K9)gQUs}pwqtEA%5(t
zpKFD8N#qbk@eQ`6$hM}-`GDOITTv?0S3Y8Ke}6jnOV6`QQZ#5coV$Iv+6|1|n;enc
zo;}0tFCFrIt##sqOZ~v;>A0|~%1VRu%};5=!&hrCrO!}yX5=s{;_R7k5sc@ByxQKG
z`0wljRKN@<+3BD6Dz*#5niV(hn+IJkzfLeSghEufx82S^Nf1g|Up2B>!acYr9bw$_
zsLDgB{2>3XBKd(X_55iA)Fr5a0;_LJ3IAQxfHXDMrif2=?24Uw$oVWM&CyDCR;5Qu
z-tVpMY3AHy*Cr<ZGNxi3qIxD}PwYLdjJI`wUd+l#m=TD@wj2~~gT1A{xykAO!<imM
zW2~u&q*QHW+pc71Ck(Cj@2C_LeqGwYu0?d3;8VEumSD*bPOeoxvG2fjhj^!Skp`NC
z?<aIrm{yv1&nLM5rZu-O#)+P>MA>9yt0A@zehc>OLK56V%3?aC=H$?uyh#fz8-I@%
zR%5|+VIKB-%Ga%Rwo1Mg;Y0*nzUr-^VzGyWVE2u#y>HDM1i4uhXVE)qWv=S8rp|Sp
zW_3@FNN_^WzdLaiA^o;|%~p*4lnz;t=g!9`uD;fz+PWY*<+4$?z`?~GtlJBcV8iO5
zoTC(Wbzg#RLoGC|F`AX9m=#*fg4SM9FA9*Vf=W>YvoOr8MswIk)D9nxUA??;>!#|d
zd>M{;=ldlB{<8-aojvVd6WL7o5^lo?Bexx9_iEMcCZ0kP(w~0u(ms{-q*LtMN{}!U
zBfc7*&V=vV;+@;UF}mgh?`Pk;KPxRN>dBHERh#6QR~O5o6-JJBHtbCIO-Yv4JGgt}
z8?Ma4=U?-3^ma;4CT^a5`=Lx1UmW)Rs&{1ke9JsTf@=^{=s4NzC8;p$gS6>rR<)5Y
z`V#c6-#jh<;VNsj-oaM-)8S~H%=jyL-at@7h4#Uh;`QLpc}HZ)U4=?|rb&q0WL|--
zcxcZynv<8bd8L)_K+j|*IHXN;qzUJFuB`5K;Oo|_tBTg9)$-jUS1wAGPvmCUDg5nz
z-Vv7wMPQL7=+0Eff8=jHp7`C!>w?f*Q1$`Nw>_rs>k%tlmJrBz#D#3Zg>9;qRcQS1
zOw^8PrB0V~n4!z>oU}jR1JyjTbpEx;no%P6Z%v6CkR}xIxVR~bO0r~u3ujA^V(9Vn
z0`!fEx_35oXsRB)#&k=vKIt|kDyvJf8=N`uL!?FE9tP_N?p+ssr@`GF5L*`=Kwi9H
zlm&4m9nFh034Cf+&slg`TvSJa``7fqFc`(xH#p$_wK?09SL#xh|L~5K%E35I-kV&?
q21O`vpK7#@t&J%Hs-6D^i2FPkeN2bsb6Wd<j~_zcRIgswIr@KP!sg`w

literal 0
HcmV?d00001

diff --git a/packages/context-coloring/screenshot.png b/packages/context-coloring/screenshot.png
new file mode 100644
index 0000000000000000000000000000000000000000..89665b77ff45f1330b754682e6c787d22c1a56de
GIT binary patch
literal 22006
zcma&Nby!>9)-_tE6bfxAR;-0Wid!j`kkTSWQ{17ryM+Y#Q79C5Eo_PgcXx+i!HT;D
zch^hLx#xWE``v%;exAtMGUgm(%{AwG_S)O;tE~9bC$FB|xpU{K#1|3yJ9q8@w;$cd
z_iy*iT70y;ZCrhoRusK${{PvaP$&!rV`gUl2!(#SZ9qSY|NXxGD{5tBB`FNW!^49L
z%ZU8l_x1LmjOgttPz5n4R7B0Wr&c4_4f;vZ4e-zqg9?AOs%bJ2meCacYVb+T4Ej+*
zPyW+KaaoJ5S-r+tJ3}$#KXS_P1)t6HKdU$eHs=aUD4BhMO4-4ELe+wDEHq_4BBPBm
ze#k2OxEe&b*00yYV-UqtWscU8+q3y$a|_~Te)h5--F_<cc*tl+{Qfyo?Ud*s;{vZJ
z(}#+E+o;sj7Z<AOaQG?=l}{e}IoX6-$y(0VvPGzScq+LkBIIR039J0f4pRymNEr(F
zick|4vy61}S6dp%46U*c9KZyQ<bPF^(JC2_>d616Cf8g3<79uDfv<Drbg-X+NGY~n
zRM!peEc?aSHehN28EhbKpJ3fmBOBFcq5e6bI3(6u+`6c1X~64;t5lh(c(|(Yz(hjO
zpHU5@L&J12CkwMiTG2$TQuTPMQDUl1q-z6Q_SbM_Zcon8c%eUFShSMzS<kmMG)Tk{
zn4XOOtP|;(>19`%=Vl?6v{bn@67ms-{8m_MlZZ^|&XzLJXJ+Q`>>F#qmh4QXrt68!
zHUIOYsnadi&bYct=93sRyMSJ5b78W&k-A7utg@lCY{c{*|9r>M$*!P+@7rC-u`;KQ
zu~bQA6{T-dUu^<QyMMIyWI5$!Bo(CmnCg-7PW4TWuOIS?cas&<&~l#~32pE!t*{-=
zHWZipD#XETTi5@^qXC^_tpeBiUXt$W8G<Tst`Ah*sW-Ft){AP)h>p@WH;}eOp~Cup
zaelRub@S~CSJvQXQS=IL^08szm6CYR+>5E#P6~pB1sa8iE*F@veb#XZwq32YRtqd2
z?uhis&abEi3>*VX2inpb!t&C;7v}2wmzE}>+r!$!gPW5^lRYMr4C4KRt-JeCwhHJ{
zACLAzq_Z`bsO-Zwh34Dnf9fDFt8nKb$<x<7OcGG}drv4X%pE80+>!q!AtI~@pV&;m
zSicaaZ2NPrF?z?6UbGNbjmShwkG=K5y*HZRIkZ$81u0C^gwJL>=*AQNmiko);$)#s
zf#~Mp^%ElFX!q(<EzqPRZ{>)KUw%&rueA~Nkk<K9{Ez=^ZR)nOEsl5rk>A!j?VW`L
zeD!trNIeN@!0^J-%rcZH;vYh53k!>PUg}8oG~CnO`4$S^QYhU-iK;swA@*gvdzi|?
zr(zP}82dC2hQBMZa(ckaEQ6<GcR@T0@LD6TFB2j5J5b}x9N(?z4PApVV5(@c*P`N_
z2=&3?Lfi$#rJyiw9!-f7GGC!On%D|~aW_~v@GizC3$kZHlWFhB(a`Mr{SXT&1ufrr
zLk7s}gwEaDDzICV0Mx@6Gf@%VLJW3OX9+2Waf>1QI?m2Zq2pFhC6AnV72=L4)Qj@h
zsNNxu3CwnoIoDIBFj|&TgzgsZNS)5<?g;A^CJ09gsf`*Fd=Fh$vwt|5Cnsi&8R)0l
zVcG#IuS&wLF?+jnrzQef7>0Gx62-Nr;%e!t&gPEX6Mz~26%9d~a>tf40Un~po?Y9B
z$69<ZTMlJ)qhHSDI4z6%h2C=1v?f~bR)}-@2eC7J+#T~?oZe$^W$4WB!YIP7UG6+s
zR^UM4biP|@VjI3{4MoI4!I48&4X)*@nT}z#yPP`h##96BM_n%5`NKF#7KH2q1N4qx
zVIC9or8=vXrH~!OwIX!-{&B7wa)VFP>)4_`uk6o<ECss;N7c$g>>PYpz!H{S=c7G2
zExB``r!rY0BnY9#mO+k$&(-TGuGWI3+KK_~o%b3S3Cq)+<Z>2u=+TK({H<2<l)R+b
zb9MFcJZH7(84qiZcQhl&r(NXYGUo1~jL~zx)(zl^+`jK9(1EY7U17nYWd*_g|34m2
z9R*{4L+o`9lhHgTn0ydyvN!94vm0^*wJ5%Y28=N?C6WaL>r8F@hDw%d)7q0G@3r6+
zha*g#Ya_-JGri!gG>AQWyEWs3Y^`~{8k}DR)q-31yV%?JYL^aLP=|u0YJXZzcOPtK
ztt$<fqw#1Ce*AJ2X&9FB;LjiKXfQ;=2mIKwyb$L@Y{^%jI+nNA&JNne8i9+MWVF%*
zk&9AAD#yuIR`kI|#Gj!OIm>$M><ELeV{#~&)3r62hAe5#D(wU=a{X5jBQHfa?`YwW
zEzVt}d>@~g`V4f=w_swDX9l^xH;+4}@MohhoK%P(HuC&Rp8aaMQl7@O7*e|`5@TAo
z2CMNt#-oZU$3nayZS$tZs8_&~%{9I*;JVH@Rn>ip1rk^F#0z`^H2m5gVa}j9wt7FQ
zV4hi_#x#9hgXnm)CBeT~_;lAd5eOs<Sv!pUD}AM!_@PwYA?e~P7;Kzc+Z6V1npsz$
z#5Iv5F}@5cUXUY*EF)^0KMfK9uF9FLg;b~s-0047fU7ppFU>65NGnMPu{OKf?FN`B
zR7IRR@=UR}d~`ziFF}j@m%FCF{-|tk_1_BomEicO&h(6<@m66&*Z&X2VPVLXDXB+8
zT5w`=fj~8+!S%2_G6xNu`wb;}L>@2M!z>Apiq+(CGJtXWt19e0XD%&__}1BYe^P$Q
zk$d$LYqt1=l@Tg8iL4`iIpP~S>^M!xd1|lyp+-ky+qgtO5Xnt2dFSS#j$x&0F3Vd4
z84Y!p8}d4~+L&H^zq!MeJn6r^{=Y{~na*lIm>ti<0_JFf$ga&1eQC2}I?DR!)27-*
zZE1PEx+*fC*a$iC<!71kt8ZJTVS1{!xsPlFAYt^m&4~Vcq+95nfYIk5<e(E4!`9R|
z+iK}j*Qd(HX*?cc$GwAaNTDNvxgz5iJD0*FRz>b?(M@o>Z<Y<#1VZNLD@JOytyyBW
zP)SWBVlzvQV%=5EC6AE)d|dsuOP|^$LLkCm<g0~p^PpwDmdDE*{p3)e#9BzNtwCb+
ziGCH=BPnU+_8+FOx^m+@$5@aLp7fPp*^7gxes#@Wkf1&7h1%U`70W#5FG7|dL6)v0
zV`I6m=2F4xmjd;DKRGS3nAMOz9brYFomb?duNjyZn@dsJtV))A6AmH?<D5qz^^ZNg
zEXPj>kKeZ;{SKPF6yQPiY5G<BcdWB$dBkG=Z#0R&H0O`ser+SPA}7s&995C_mJ75{
zJ~#ew3>uOA%KC4t2g5&cD7O6mB6;4V9u<$hV$>QQBCOBc+wgYu=_u^24zEB3()mtC
zIKSdli~cMff-r<Rb6WES|Kz5)ATp<4p&&v2=rDjiC;Tu$<mT65_b|IxJYWws!*+D&
z#cp;f@Fp=#e@s5+Il3dP1qNGaFha@~Tw7!xB$2Uk+<Iar0FklP`d*&_w;o%Q*jc(|
zbic48HKM~y#uRp%(q2F`LQdWCVS|hAjE@Vp-733W2jW!YhfFB!Kwg!JbM<Y;p4-Dj
z{-sUG#Od0JbU#2#ZBYt34-Z~m|3NR&eIqTX96~k-?1~2IfYP`>;ZNpv2evMDx^7Es
zDz<;~LwBtW0EnIIl5D+$*0^NO!&`a&4d<>JTa}tyiVDPOX>r<*kF^;r(wz1vtU@$o
z%I=SQpW{LP7bY{Qc)fsd*|~+CsL{e=LkOJ?6>>Z1AXrq=18cQqYWr}@bkC!4Vu-e_
z@VN8~XD)<QK2Y`e?MgS#-apy=43so`N6%RTS}?B7E(#ozWUBg<YAf%m#IDbkpYb!`
zkCf=HMiuB!Mx3?I@oeCSP~F^5auW%rE0_gVVt17aK?k=(kMSmYji)tCv?qp{b0k33
zQkQ&@$Gv9P<)V{w-mIV~e*g6cDT^^0=t2eFFTJ`|o;Ewfg!s`wI&28R3Q_y7%?j#Y
z`W?En>lWuE#`(pRx%|P>_)23D@0wtC+jFI!uOFq_;JrC{8Jb3{5>Y>xI_hg}S$g@n
ziGwuBTX|!k6TgL;dZ*P)w-(KY4~f-nhPPmODJ+zsS5P0PdGDi)$&!g=rRDSCv#l2J
zbhUY22f5&OZXbGq<x$On6=juPJ#8SdGTx$$yYvo>a~{)j=NcK#p(0gstx#jrj2PO>
zbyHeicXYqtfK_a(Z`ig63+C7t^93|**HVb*#}+)+|M9089-%dMp)Y-U+E5QeoSB)9
zc$kqAs9J-8vVv^Qx;|@~m9-Hqb4cHqt+Asli81F`qVr-W@d2@JZlU_0mE+5Jz2=UV
zhS4Ey*4}g2R`cBr%MjzXtmXCjV+Zi0<EVNFx6_nbVnmyov=g?M&#ui?boO1LI+=Ab
z9?~Ni_h!oK#@(^({RXrQPsR1aeEI%_ic-GS)$Z1KUd<RHp=hyhJ%Ob!1^dUnmi&MF
ztRQ{*T&JMq-yY|mS~OcO|JP;H5iN`Vx;${82X*JAJY%zAwg{~<;qz=N{$~4=O?-v;
zo|1g(9Re|<{Uxfjj~a~Y6Tcrf=sppPz`i4Un2NT4iEqde4;H`sj~h2!h9Y?3RA-lh
zq5P+8Q5)mN%PkX?fnxn<BVIVHxr&eUWpIb20wN}szj<}^akX;pzBNhiRFw%!oY?Ab
zwwZ41R(^+%$mJj`gX9_(#&oh5v1Sn?OkV5s?*=r~o-~Hc){^d$NVO;ou_~*8(b$J(
z9sg8kLpMSFGM()`8!x%%oHb+gX0Crk9iY$^54Y#@Oip;FUjrXPb|g2&0JS##*(vd5
z_J*C%0wURH_LO8gV74sfOUbKG<MiyGf{j{&zx5sjjWXO#4TA*i6W+l#IWl5<39H#O
zA*tf=iW&LvN7FlUD&V5Xy`F?|s$Q>@FxDzDjf;#g+z~JK-V()my_}1ab#t?`dGdSO
z(6xH|da8~MWIrR++vES_y7qS#2yBTjo@Y<&)yti}p%cqF4%QiKM)MVhzx#T~tdRB$
z#Wx`yN=HfGy!b8a$eG0ZL;@ZiEbmuhG%&!@oT3|&#_N1^50G_oc1(J<E3(4H$JAvF
zdbkRD(!G4@bGL&IYc`x3uhZqV5x)PZd+(^t)H@JBvd9p6;dxBrnZmQRMjc?}7=7j`
zWk&<24My9W8r&Mtad}OTPuslqn_VGX%=-LJ>X0&Lj2~@e?*rO359@pPfW{47G~0rN
zd$7xVi=XX4#15S4OoB9+ckY<N&bI2L)e-GNio%KYr{2}P&Z4+4D`)lM9@?%FRobWE
zxK0jBf;(`z6W!Bl<!*qmoJK#~iP-a|K8Wx(ejgqjz3~qy?&re?@8=&TYd@H?9AOff
zR?~NFYLK90FFv}<Pi_dN(~!7hH$nhkXr2r2lkgKLr<E~Nz`(m=wc8a8KE1`ih10w^
z;u~871~eBj;iG8f&rQmSs}3+E1%_jeKXB0y)bYlP3mE5<8KNXBcux9gdre>mIEY7P
z%ht`2x5J&BG2L*V*NQo*CxH{ny!tcA41`Q-f(Y2IFgXkj@#HyB+V)e6+<nX5V<DS&
zqL`fThvL^95S-!yDo<J&esC7e&nVo5(P<EaoEsygcR8(<tH)986A?8w>47Mjb%>jY
zy9POIjCcCv&UzF4Q`^K+zPlC?EYf)hs!9$su!3#h5##SW`}r*>_1$)>Kgg0Eyk`$<
zB#P#=u5zoNIh#5dqg`Z_*-kC{6T{OGi8D%{=i6nQiMkK#`1ZmSer>!HsPyVlGvD<C
zkPMsr1QY8Q|MZbI`8LwWwqf<20Se+Yucu4ga$xg{Q^aWm;{ryGT`SL?e2-})4Z-&h
z9mG4k@hLyMw#=ZZs+n1qckcN$OqrXbYP?yi(>?fpkonjpk7{L&k)cCr^-MY1wz|F}
z&1jnAE9}m1Q0Co!A~O8L2aoPUJexMPU?AKg0-{YxOv~HCR%mQ{et(gQvoa(0V8QyZ
zy~G*9{Tnov1uVWZ;m6d!EcyBF*n8D*$G*wNEi(Mm)xx_cN`|H|zy0=Um#=7LI|7I>
zJz|2n;3yRp(dve6RO;BPCHi4`O)`46o_Uxinlps0M-4hKf+*1ZNxZ)_Iv}5V)8muF
z@=>Eo9dzoCIRn}GvDF#I@5+(~bg#Rr`nJx|g<FY@?6G;MRk!7!^H46m5c_;{#G~j8
zY%Jjc<v;63(ynnk{=XiL`~)Yr92EoSe~1v@*N+U)6QC^itv_+;?=6B-`^$`feZXpx
z>U{z7jjMLhF2Xx2_uVk<@?pTj`*gnjbbC<|4PNV>Kfa$ZDQxYya{Y!7<a0*781ZdU
zZ4SN;f!>wk6SdR^xN8%uMQ<A%!Y;IN7z*j<fCxu;0*&8(gE(TXkNd7GvX}@KJ%oD^
zlPek1oz?Gu{lZtA3Xps+v!!Z)Tlv&LjI@cVIxkEn`i<>+tEi6!9ucX?&qp?RV&B#G
z483ssV#q}nDfHf7e*BL6^DZZo9LHY5B)DeMbZ6U)eS2!(&HeYrRuwkEh^GD|;dtC0
z7Z%=13zm4n{CO82->S6(cC`~pQC2KeRy9{JlHLXnyy1tWLk#W#KR><zQ18-$r5j6j
zlhUXxud4)t&d5<84je#W+Nd}YxY?%(u3hikcA~6ag#`&YeNC?UaUC8VslX%3sbs@9
zZxU5+B=>vff^oCjI;B5l7oR5J`0u?GOYj}mr+c%<#&J3|KGtT(nkFRQXTBPzx=DL~
zklW$Ih{leYF1aj7)^)kX<%@HBEeF92&WhLK7fZ2qZmzT!d&BZ^5_WTINl3S7-}F%z
zcQ{6+QGO{@Hn-s#nIH3mb%Ad%ST3;@>mEI~Y-IX{&l-F*_OTQ1DZUu<m6tx#2r$lq
zi<uS5U}8pyYyEJ{R)vHW`nu>+))MyMbc9U=4jb!(U6{DXGwGWmQcL06mTG%RJI=Rk
zIu0^esA&QgJ2zSjbl`pN*KQ=@)YvyiWG!wZ>P?63j`skWyVPGXgVCPY@;-ts)cBTu
zz5%K8QiPs18P3lAgC^Uw4|GT4WH9Xuv=}Qca9PppVY^N2JDO*HHTL7qXDSS+n383J
ztRu`RP^aNELFY4XK?2mBT0Uf8HUY+S*1DdR2f08ndm4LeK2Rv-`lb5|AC@Ml|9{Z$
zcQfXIwt~`{xNWxFEZ7?}v+b*C9s+)Uh&p<+L9=Qsx<~lf`4}N>2=iA+TNz3f@{3Z_
z=nv6BGasBLmN3{ntCtp#dH@dVl74eMIG5$Be01%&$E3=^OAasxkd|zKg6X!%M8)8J
zoQR|o--A#DSQ|3?CvAa$Fq&u-5MQZ*<^~i%x=$xcLjI^}IvkaOR({y-3USa-hInNy
z`jwg-(}-|iw$a=g_+X~C>tJENTxNZ|hVzJGGGgAvMEv38*#e)_`WGdVqyyH6JA}9L
zjvd;=HmCT3v8t1ioUtlyO@h|3J}M28|4kg0CR*~{y|C>2T|aT)FSyI@`^frZX<=33
zg<8dj;N%;gGV-h*gVa7%a8&PRN<0BT?M}+Bb)#>_Egy##ODuw)7q~478NY%*dZa(l
zk!fZa$kqDdQ<wB};H{r1Jp3)myhOO_;Jn|j7uP6mbXT;lBoS=mDq%Z%w1f=@IMhm|
zqNf5hy+P=E;JgY7X5QSKxzN1>CdmND%4{cxhLQ7V|LbpfK5nX}{F-uzF)~<Sh|5B^
zL?A3&!F0~FZkmiI^yXqOH{nmCPm<f8GHWXh^}v5!Fa|v_Dr@*ti_ilai54T=RAtk>
zP)APsR2cCF516q`jpy;_%|{dF12h&*g@h&Fdt~fn=_a|_JHVcxcF;v(f+P-XW2?O}
zVqm|A;3_3&c9!buF8i3TnhGCxmL>V3KEUVU>KYy?c^?+A)xPjZoKe?O1Ux{q``s<}
zB*JT*aV+}jo@R44NNqA*`4wE<l()T&7+!7d%8~PJTMHMS0-eLX;Aoky+M=6_Gtp>2
zjRkGCk|k}n*fBi?vzAh{F!a3EQjsg#4$;J{DQ|(F!wzx4x-`m?1cV=Ci|jl8&-q;s
zV7OcDM52s6<ZMLX<q`@QNjisdd2Z_vqNeD^Ky$Xgc(*IkA^O=1zumXvpS(d5dVhN-
z>I2aAJ+CQV{lEz@CaD@M?r^CHWZ2FiDX^GPi#t4YK{61cN*$boC67(-t`#q?_2wiE
zrq(aaU-D7sSbwgGv_S8gGBNv<1&c&Ysl|cM4*HfOT&CT5+%~NQ(}PeA>zuy)z}jSd
zo%woV)?8c(GQ|1?P;(MWhjzLRf}QBEHpZ(&Jc2vXEGM-&TH`Ja?i8=sjNV`NQ*DCR
z&DN|4d_Ex)B{eeDYAQ*29yz<mZG04btRvrSgs;J6(VF|pN~>++$TEBBCbA~Pxx{_H
zW3TlzxcPo>6MNNN3Db+$NxP*rEZKknK(l0w<)kFFKs2kUVm}?VP7KwoL`<b*Lxf6+
z_Pl$^%wcs1wlT%NNu?m8B0v$7H=C79gZhbbcJ93_W(`mb&CMxXCsB8qkd0nc+gS?X
zuPJ&S9|$V&>}n$1ACPS&`k?<DHUs0)3G2(r($XXj$<oE<&;s<%`3v9nGw4-a+r<fs
z^(2db-DZ6y{3yA`2<{e%&w|fkV1^tZfnSym=Z~BI=Xb-J5&szL*c-olxt=YZ3y3O}
zT2~~oG!J#}?t<@wdvlEC!7*eDSv#$2<J0al9p~hZRoJ^QH&?gRw9-9K$L0QsA)n&M
zFwC*2*?>G1%yLUj8M`2A;Syek`U%=>q6Mmh;*y%O1jpJ6W^132vM$5fW(3xUJB+W)
zvpO$3T}Um1HrdQ~4Qd^n6E9>shy<N|{uHewBHgZ~lD~1hPY6k@`HRy?RBkZco?H5~
z#JKi)U(}$l`75T1n-X@iw=zdf(?067ttCFbmlD-Y<&By)Ok(G`A0v;u?Z6H$crvU0
z=PQBNmH`cz%6?n+E^Jw$AD1}N$Hd-%cl<GIFbdQh@wVvk|66JUcHc^{P|WdD(Q!8{
zZ!Fhtk!<!WKXx)b-x%uYAmU{m-eb80saSYx)IX7ILOl^ICAO`p){2juha)C&N;T^5
z!8+B!y1ufcu>Ct<e2l-l!H?s08Xo^po<Azj-_HNsn>6hI!LU?C^40g22J(fhSq_Q{
z*E*~P%Xf`>?Odn1=mJ(_{Y`=$ox4A6Yu>Nir{He?5%j7<*SDPQsk$kh?`+^A<Ag6c
zffl<#Zxxaf;~Q{5QnZm^q9Athapp{owYMF9d@%XVdOJ_LR`+<a?fSUr^l1s+hGG$-
znJwt7u|3dM4q{8`WU?{%Tle&UJ-xv*o)av3n-g?jm|OA`Sj}$eR7sXHrb3(<)>gnH
zY5bj~_EG;BD}37(N9i9ZEH~O~82cU%Z60%~6bA^Nx4M4QZ?9}5`nQL#oJ?=h4@$-3
z-~5H_urE+(ECJ>jCn5)cJUoUx2o{_vL7amsKi=HPdjMP&5lIhpS@>9Tfpufr{|6Aq
z(|8rwVBo?nQ-0tGz5CL#%LG$&bQ_++iZI(PMr8~GPK?^oe{B~HmA`#@Yg--J#|M0O
z8+!~J$Ot_4tVQTFDKD<CS$b$pR)O*%AvPLzu3KvDqkvm(-#V$nSUM-oSS%--#V{X%
zV5y^Nf1wl-Mx9=U?U}o^9JKe>Y=P!bzkPnw<=?u-11_@P$x|t^H-=OFEJ_qCryZ-(
z7b@h`*Bs0IkEAgKiba9M;cn0qY0*Za;xPhCwO6H9F>GU!Y{SgA!({F5#;8N#XN~Ab
zd6VgrKIm4DwmEXLKvA_YDe)1MuoStn6`>&+kk<3!ZwdQP)}+4+oDRNFdwo8jXo2op
zznWJVk}#bq?BOZfIt>U?LwG2oOKj>wM>LC}#bM=APHjY{T|;d{8i<|9YxKjAn2rm#
zvxnGW-KG0~5FR60tBJwros}`FL)%)nJ@1SF<GFMY)e1bmd$O}o#Djzh+jx#P?bR5G
zP%stSlb*Kf(b*v=9u2$I)Wq0TG}zl&w8N|}y~4PPbS4=gb#qBC(6~vtjXLplc1tg2
zHsyP9HGskwZJB7RGkIIP@umL$7K3t?O2&ZBciUY)L09jn>G2>^v6L_s%F#n_9I>%4
zKj`LnKN1ymeZI(k2u8C)7vZv#R`_5VZH;oHFn@KwV}u~xS(zDXv9v&-#-R?TwtG37
zUo5Vu2+M`k1wofd>q}>TI3b{{7?t>wn4j1o_RL%;P0K-BM}qx|F|TH))^ttJ^0*Dt
z?17zQt-e@ResheKv%nYbpT_G>`o1Kqb$SXsyCO(BjbcvVL|{CX8QaM9l>RwPn=Wec
zB74B9nRc$tuH+iORlC3tZq`<{ltm!k7j4LP;^WqRc5s^PM^U<4Z-*ms53Eo`aWGmX
zM+#4=3u7IB1_P&XJ%QUP^ZA*ePKJEfO$U?ySqW+op`9<_2Tslxc=&|m$CIyWX4qiz
zkN||qEZb9H#Yw*oVHYS0?zKQS`l;|^9#Mgmw=@-0V9zn%odq)fBvrD{W>%BlyR9Jm
zRU}8dpS$`OOOGRoVca#G7R?J2L|X%Wxd=q?m7>O2>f%cs=8>#-+6xoh&tXoj{E64K
zn2}N0f8ys=Zx5nJfU+FH@wTgjGNJmeB?zniK%bU8xOcO?5LX`?{NAusT(BhX`6Q<}
zd+;hBKX}X(NhnI&qjY2WmNFPWvcq}%Pp}9$D%taL?U2;uR6qsFj5RpK2G9xIP6BIS
z$HlgA-+&qcN^g__eB$f42iUauUF-5xS{ydk>8!ne?Yn?WS@rzOtGws)E>-feE@3>;
z`}68p9<5i{LK@7AGwNh^9$ZXZ7}s$3$elx8D{l~^*MDbte}~673FdIK`5NfVXBapX
zp)g&0O9c$=8D<!h`g~4h9Pp72mo9H3a?-`y^?n*xe?LZ+I|#H{W9Un(yRzwNNv1=)
z;5g=qGKZ2_Hjh8ubR2<Fdm5t;Wa2&OtX{l9XLyVYUd(1>a|GW!9L+r`H!Tm&O)Jmu
zBTcc#V~NkOVyW~_@8Kkp6YsR?Y|fa&7g*#XO&pK45(ta==55)8Kg;mt(K~`+@`T10
z+JU%lW!}z<`V^93y9tG`<+C$eBk}@`z##VGGhxL^0+OvR338G!iJFaY1s@$w_p}(}
z?FhMM+#4Vo(}fOt7Vun+B`|EN7IBA)SH-{cM196Af`8G;i;Y<+2+`AEm}D@gYkzP#
z7yYij;M9*C>f6T`>bEJ7*qTH(Mue|V<+E;))1E~Pc`YmK@8PHKII-pA<V;o$27-?D
zFJ29W^qMw%W545jLM7+2;FE~Ywj}|nDDUtGun+>g46!fwgT0I|9dy%hi2(1d{`OCi
zVbmojSGL7n)UuzA&<qm0Jz!UcZ_`(lgFa!8EZVs}5A{cjo(m3oKLjXp@Pd`b<@_(Z
zodQ0sXXk7v9a5j7O!p;TfRExX#ON%^OdE@K1C!V`-@XfauNz;qLkxTLb<pw#+ou=y
zY;&TR`<d+Ll}o8LhhG*Lh;{HGC#cKxMXFNTn}Z6mV#et`#UwWrf4EN%yes2<$54^p
zB-zc9xOO@&kURWx5go1?2$Is{1X%o{zQZRTiIrx##4mc)Fzs3eo`WwW<kG=F@O4q{
z*W*hhN&A7aLf7J20>@xDIYvV!d~xl!<3t|T$u#(So81d5g{F7W+r1=imSI^t=S?Tq
z!6hJgB+fkCKKJ{dXGeul#iTuHrF4A2?QW4#O^HWoin*(ZegCKNzKrLBDImSK>$)D9
zLW^<PlH}kHmJxfpiSUsqN$sJZ<ymEFI>@m=(Em8$vmwkli$D+aEmTp(texnnFlqf@
zjQa;Y1kfihzk46{{Ar2kVVe9q5@4)g6uTN%98L-Q-nX5|vFk9P|29PEn3A=a+O>xo
z54I`pHr*Q=QzgkS5sV6lKEFKj_e3$3gPOTd`araBfr~c$Yu7tRg)~Qwy!zUs(B-#+
z0I$u!$Ky;1NcvOYQX@!FabgZFDf(B#HhA${lK+S*5rRV5PkOK<Dc9HPnwtjxm?qL*
zSJ8_xsN~ZxYP*?zI8lDhh}V_5q31eNE^flCnL!Mw8c(EMwxy5G>rm8?YlcG>vD3UW
zvZRxKlS|n<4aO4R@f@VVwpzP1`{CkBl?GtzSAdO#GKHX5FV}qB1JYIR-JY2f1W8wE
zOGGrBl>Ntu0#&I)E(KgHI#kK-gy%GvL{!J4l72&~wwkykfNLsZz6(FbpsSf8y;QC`
zUM7|Fdy{tLh{B?mmjh*(Sqhm8qm5Q@B_tI}Ia>Qv#i-Ov&_Se1hlKP*j_Nd&;198i
zY5NV%k@5PypmRPQ7P$a-q7DVB3G$C|PVnk30)ze+6Hb$#L7Sz!<!yn+E61Xu4>w3B
zdg0Bi<ah?Zv?Aw$KqDK83?y==N{DyfB{N?r_4niStz`blluyhQ8!qWJO#OtKG4m^d
z*EX65A2j0z2)6s5zcUe5oxHd^M@)`fsFm}`VC&BeIY@t4Z?EjEY`V&83;t2|`c{D<
z#B7THesRnpCuH7)cQLM^Qd?y7A97eFy+Y43+0MKF;y-}ThBnjJS_>aB@<erlgtGz)
zOLHzRruuO8gYS+SR`UN3>ZL{6dwpgCvM&KX>*1s<y!Y#iuA<+1wRO^yXIuAP`u=78
z<W)@mlhDb%Ck+j{axVd`r+MxT4S5c)fZe1PV=<NX%@q6-gZExicG^Sv{`-EMc+<me
ztlGk+RPm1=wGqZZ7JDH&WDbpv<US#*gdIHAEm7oKwA=k(n?{ipTTFq?MA)EnZbwfs
zw*>aBA>`we?g4I2uwM)4gHE43n1aA$<N_bejSHJJr$yY0%3RHo2?C5r4J$#Zx6xF$
z=#^Ga*&I$r|3flmpIxRQ3;Q%so+<lYlOuVIp!T6RGxvl`3g~0Db0S}42PI^Uc&Bgb
z;j1It`k%n6GlM=R=Keg_RXgBc-C${3_fN6<p7IlLH!o#jzi+uq^0bD8K8Cb@XeeaQ
zwuy?|3Qcwq_pcs?A}ZfDOjpT!a4s^sw}`9_C^q;qNQyyiwAz=&JoxfmDDrzuaGXsj
zG3SdDxl^ooEH<`}{)fb-n`%YXWs;v8{1#0G)zXutp<qTUf*Quh-~}D$weO8Xi0q~c
zIw|yyNsR+N*xp)@x`}AJ)8BHv2l6}iuLt-lD}_>a$N#+kIyH@2l(@n<xkd<YI>;tC
zQ(WCD9Yb+7U%6sg$(2eqm23;%U{5@kreW*L*YEZZh&j5w?ed=xYwMx9VhA>Np|IRA
zREL}#ZfhM^M|V04IdXdxhW(T(_5uXsE<Y1J6AKfoS*rOsDJ%c<9W_wNc3PVka;dA*
zY$z~)24#94W8f%6jbI1{X~xNHg|FSqqt-O9DtS89-o~xIeo{h9zZhr1e<4WIQ%wM#
z@3JVB%Ev1e81^fpmMN}RPtuJXrEQOhV$cJty$IMdkYH(#pf3s!0;Lxex8O@DgNfPM
z%eulzVGy^dws4~V{49OxckbAH-4RXz3+w*TLqj6P1r|LtC|YWb7*uiDM28b&zMeDH
zkd~yia!1y8ZMU(I@4Z~h+0^a{e+iS+F+9<W&V?7>)(U2x|CQsW4ZxPxEOa%WNTeje
z#BEiEccB;qv_Gh7PyHo{lq~pw3>IArpO6L6&(llx3Hq=GXdl#q>GTL748OLI^9V<W
zKZR{j7@u`J+!`=C7>l512V!3tUfU&f04(FXtzPsh<FtJ~#%LeoZ2?HHDvNq!2v*H*
zUP8w#Jhl4kBK39NqgyU&w=y^e`2R<bTdS?rYw$s?QFW6yzn|8d!i2a-L5FF$Tl(F{
zt6)6stm{ES$@((=i}Umu{&9%%^9JBZug0k?qSo5iNZlqgHHxN+{wd7ysD&i4eH```
z7uWT;Qnbo}nArBm@!npk(1o9Sk=99HI;#anvpk?+wF50k+cnk&-5QZ{bS_&q>)2z-
z2d=M;`j*1yR<>D?rUF*%&#W4`J&*Txy}Nc?V4hYFfMB#Xy%4Y$5>e+SK2my8vGdJ&
z)nGpB!#M?s*@Eo$iRF<8FTE`M!!X`SYw)9_*_)!}@e0YKWgo2)c1tzq(k}xsHpJ;C
z<g+8G{Bm16>=(cFzx86B+J+SO*J?^uNnA3QJU<_>tY5Lx-;-CdltQCelg5kkA_;TP
z4%<4`M7HbdvNDWhzr*D>HyO%QzT)>560r8~2PXiVLRw#%T#gK!Pjup)<u^UrQdSgL
zgW`!PlPjj5tdqmDgIcWG6<&Zjj{}3<4`!JZ16S96Yw+d`Y0t+)Hv6XB)w-wZGc&(k
zeG+8#PF!S7#rt{Ocz~YVzOqnQ)(W1$7`tq{3@y|e19iqv+X1n|*7DTjwvNQIBvWtW
z4!^*A3y13I+iblYDcG0b={Ru*%3ozQFq5AE8FKJC;XC@6Ww^!mi`D}Bz*nnrFYtaO
zAtpWNp2Du;2_d%fw(wLwRw!cju&t{Mz3X43mn8TEV-3ws)VRh@$jtW04b!;Cmo4Rl
zMAlA>!P(Bt@=^|2oQZ&gXSs(~6s~D`M=SE2-GQJtAV~^_PYmF0&@aZ6B&Ss1-rC8}
ztfXM;*dbl|R{Lv>N5I>K!hnS=^ABw=&c#(e=gjU{GpC!A)AGN<h~r}Tz3DAkFfKL4
zKR*TrVJFSa%*@og{2ybg6ukaS!#0Y4b)Ujt#a@#FGuu%Md3V+szbYOcM(@>XeFC>|
ztIoyMopC9>1w+f<1<@jW^53Kwvw-OaO7_|4FU$rajyG<XHh_AY;rCsq0|&Ibfi#fR
zJw*x>=3$A;CL<j{;2)9v?M1Ug{j5&GGazS#q(?h*Ahx6vv0D_Y51U)_4}@UVKe!Q5
zz_L#&kNRjz@_7+2T%AVKJW-s-hYm5a6v*-oeT)g%@)x@~nCEt?i-o!;VH!H9f>5ve
z3hfgt#?Ffx3+%>NcQ5l=DnNe|uT@76gainSLtZCe8=I_XyJx1Sr7fRO&XJEF6XY^x
zxlqFySmv^p21PD^dV-9$A1sN+R~hpkl3$zh8f_225iu&vf!K!4#BOsUVakr1?_mxR
z&!jiham$s>iD#8U3tv3Mdqndvo#{!rBH^o|5%D&!pNC)Np6RT%QH$&a;WT-%E2gEu
zcYn<3%n36io`Ly@u8eq7Wr}&qB91ORKgfV4$;Svw1QT`YIYlt{-qxRZ?_U!l$|Tpw
zcO5MZ*1k#CROfzslk17_&$4{81Ekk>-rykAVc$TRzqV{_4SoY`lHJ!TdpE)`c|XL9
zPY{V8!VtfvuP{9hpS8H{&ch3s!m&>DW&_JB3jFJi5H16T-%7PWwq8mlV8|>brdG-h
zxeB5ARq2g5e|OtF{IZ6wJ9;vz5Ve!}B)#)OsLF-^If}Q2jwViyMog_(wyG;!j%0dz
zaumsa0H^;TmjiM|tW8<HB#YrOx8FZDUR^_8&Ttt_lB9h)ROA8dl>J3X>x0pQAxu|?
zDJ*IxkvS~nh6B?Y6d;y}QJu)&irvMlW@?Zhw)716!IOX|&_dq86~{P0oPo*?g;gAS
zyI*V448hpD!jJan?V{*YS*nq3Z6zw^g5}Boz?61!t~uG1oYn2`BF<@`U7>L=wB!)k
zC0P&rdL}pd;)oj(wIk^_9@;E3OKH2lMC3;xQZC7_^fmX-gOHf{ARUP=LU=YGWcj_L
z6G#36aZ4Ea_8Jv4%=+-F8yViyI@d}~L$s>gXIKx=kGCj4A;yV3r@A$BmLKFlG*msA
zlkc&9$Xl8{^h@aKBihkPEjPJLNF_QK1Kb)^rmaRS%n%{;H1{E2_o|2FVQJtC>ISCI
zMv@NcYm8W`vXnfvb;|2g;l!EF3+a73&7Sp*tJ5l>1ZJs`cPeUJT`9>R<?Vi=#bO3)
zp-bD6VIoFMeMiB+SlS|j1>Oa)-Aw}%R^Z?2HEdsLIM62CbzswdD}oF}BJ?-V`YjX0
zNwzM5?}K>ZMXn1;_2nww_I*Zo^fD#-u4k(z#ITC}=<TBoG+_@z#yUCVA{N=xst8$T
zu!lOw`6VN`JcGz`u`g$kFYJ$&27i*}fo^sr0{@gGr~4vaa32Ly+iCAoeQW&+!A%C<
z{{9<t!x3cow}=NjN2P7EUbQklrVkT5vOq!$QHU1`hov45^A~D{hm=Fq1+^bpob_BE
zMI0ONny-_)im785YlI-q36@OenK~H{tarNKQen-AxdQux-|KxfQ|BD=@#^i|7)LU_
zFMG<^R9X2MR!SG7`pz|tnA?(XEU*?BXCz>3ss>D)hknuv4f@A@ptKOv#%xCZjQpkr
zE5!oLl2XhogR9VQ-bW%lT=L(OL4#fc`D<!=>YPXT0yke^X%BLkFCS5?HGN)F<H#F3
z&iEJdB^_isa&DbB&PyeXT2lnGVA>{4LGXdNAq!nP|5{-W!DY$UsaOlV?fjnt@FMmP
zlKgyh5nNZXa87Ah1`QE=`x^RCe?cz=2t1{2;E?^hk96_S;GENz^Z#nF1^o>0MC{)H
z56DoUp%A}-|KfZCe}(Ns|1-pwM~C)L%sf2qKKD=coq^(&A8=}7R<qBP+7v<ufY@?|
z_pLf)_ssX!44UR1*0kD86oCR_o}BJL1s3hxD@Km%y>cZu#4w%5=Hn5Z_HM~}@Y0J>
z``n&vW_q{riK5K8johFLkblbQj!uS<8L<{VoDu@bn~E{SeMp_=9&J;^s-X&<WQElX
z5_QZM6#`|fkS{97X$*_;0f&Z{%2FyLy!uDOhLEUnj~lL$Jv%TAc9akp3`iENZp-Yo
zmLf0u+_YZj3C76V<v~WH%&e{ZOh1$?YtE+U|Kbd*A9i%J*2veQ<_!(+@T+#q+5okq
zrRG1H9XIwPf+1^fFA_UA4cM0Dqf&Ry!*O(egw<O(3Xa>TL$t<44QFv7j{GrQf$xnQ
zuJCjnXOq4_f`$&dPg_e@p0V{#v}p-ZaBsE8jkK@#)mfG$@-}a`x!ta?DA;FKAFNbX
zXV58?$sFi+)PND{YWv3QFat9Ah`cuOlMYZG!Qw(N5(d#yTT*p($iK`-vC&11%h*4n
zKQ5p>jeblRajMNibZF5hM399|4M^-=vqp~0jO(j)2r{HlbtJm*X{o|ygEf#?|JcE}
zA@2QBX8I~2w|9`xculWdm<$G*&b;W@eIOrasUg%J+Ql-IV5yPL?-hPQ%7l2?Dff2A
zerWU#Wc1~bebli1c~SoH@$r3Dy#C<D$Y?#Ij#K?Sy!~h~g(L$%!gL(!4n3pc!{MQ*
zx)PKf45lS!llp4~Uvf()2|}S>X+@(2Q6U~4+F!3KFO$=$!uA5$b?t)4jB7q5trj`r
zWfCMARo*<J5OX_P5`@>TG^Wk&u<_KM+{S^q^7P|T9+$&XvoxUGf`4WrmY5r2L$|#i
zw^}<+l6c7y>_i#UIgR|FlIXG;2eCHvTSL6Q#WdKtTFE7#b5k3WgiY3+3?KFQGkOEn
z_>i!bz{6$v3l&x`UA7?n0fhupGmm=ApIgw^Q3AV;wJ&$%NAH%|!FVm7)FAwQr0mf3
z+<$FoRAFB6HKaFoQT+@5+wv{U#EvZ2+7z&wKjjF?v*5`5FaF?#_3GH`OcWzRnaytp
zlDv;%7mp@rKUEf9KRc1O#EXKujU!$RT>-b%RRkh&C;7iw)V<IdfO0>urM$so7@w@^
zUf!NhcsGC~-Cv^oE`t00wYtLq(poMBM~+q`NCbBPDG_P%=E0kp=iVZIeR$k8^%dqe
zC0$$_?z5!ZM#e_QNvBH{g1oa=LG4cn0CXDG2{9QbA>7E#7^4K;?QsqJCP|!2CQupK
zB`bZ=`B3!c32Zz+u$cE(L32pLq^z`r^LIL(J#QIu9qI94mZ;~SWez_>b`>XlJ+YYa
zxHX_4$F-eXX$>8HH09p25G5tMIbHLFgk4uWy1V;_q4#7=KjE?ATvj0=^RD8g-&!wE
z-I5?s;NN!fN6H3_oqccS=(9z}YhRVHaE<94<K{f{awCT`ZMAR#5>ilIkZc*s+3PTD
z2R_2%9TKsKCO5=y=bw8Qb#|g6st}d^Y8B^#YgcjHmmb_p^ck<}1OA>BNY-s3m@b@Q
zp9+J1L`1z<d6a?UwME7!Y1p(;neMi3e9=^o4^}$X<wq!Qnlr96$x0J3JUxm@0qrV^
z!mb%QQk=kjDKwkiz*dXhA&bbN;)EZgk=eW1y-kH=;)a6zu(9A<dR>}QAv6UG|ME9`
zv&DZktDFe$f)j!?qnq6WSJiVSx!i~K4C{)Eja%xF6urFqrt=73y5QKobQHArBRNdH
zeP4zG13>9791RZb3tpvHAk}#!Z`O0d$b6^b&cE=2>7*uAJ~lM4jD?`5@<ZOYa4Wf}
zU_z%u^vD=L2Gh$^`|rLrWoeE`EBfw*LVs~VH3Xn0oO1iBrF~Ms;TSl=84ro6mWEy~
zg}AmU>=SaU9519Ph8SrVvkh4aSugPd%N^i`a9Ki6Ewb@|qS&8^vF~+8S`3pm?wuZB
zZ0>s|Cm$^`UgtT#|8;;B^=h-33&`|k9a9)FEPjd&y6hNw;#Y{*4Nsw7o>Gf3HH);~
zr-C6pNXCA6f;Jm7Z`gpX#~9a9o#xvc?gs2WRI2Sm=Ks3X_xc4;h)0Ly+-&3mv`g3`
zEcu`>6GUZhQIqclfX00eggx6LA_;Gwc6xQY1p9Bc?u0NqqtN<rBe<CH(`6yL7wBjs
zI9GJaRhNXCsjsoCv`TZtp{T$Dd5X%PFXg2z+|@p)KBJMomZgTbYd#>I%Nd)A!edKR
zP_jhzyadIk2+G^5&Nxe9&u|9`VA4TP;-P8AuyA+y2aZieKjY3i*}<onVpayS<LFl{
zYbUU9?OVg?XEPR`{^$@Jz71D~hks#v4y3u>uFVbpU7ORtasl`5(C|dq)iIVS9v;xL
zelWU0xN<p||G=J<kj1z|i`{+{toKrHP$c;!C$dx}ImgK;6R$}3hqhW1_-nmiWZtE{
zN}`0STOv<4j$2kRX}rg7?Fxx)WWOp>04#72%8h$f%iL!_?Sk&J2sVi<NgQ9~805Rj
z+ADI*TRKe<39X-Bg9LDLe0Ix`WbWN5m0F-tcrOAC^jxeu>5*Olf2j{d=0_xwi|;+$
zT540r!UxY9&)f}Nl?^s-C){EcRv=Ui6rNo|cH61-A`NRn&POv;5tFQ>8Wrr8a!`a`
zT^TXUxi001*<`2Iq9C@C4u!{?hG`Ei!BvYXN@p(S{*^lPIqw&8{O%8md=h;V>@hBZ
zE(;QDXDwu{;vWQXd-S1`jVEa*4=}-)SpIWG#^xboaGeon^q#}I%b~$+xs+12nTU&1
z^=hJ*-(VD9`3vCTRlBq7(Ik~osOj1-Dql~WVm1v5y_hw!_FgIs0JhgIDrPMQ^#>dN
zF|f6~W)Bvpm<il-N*?fHerb)~!N`^ycP^gNis~q=$de;fx;?;QZKuwAfq@VrbET}X
zH<gRn+ZcA@7wUT-bHeyzg;e~*%7oIgIQ;d7U07c1<Ep(<{WO<Pua^vr#cDq27+T6Q
zoT18uPh{(bD<ZbY_XUI&H3n)(f8e=r(4Pexqh{NQdb3X$+i4+e(n&)5J-l!>s_`1<
zoi*0=RF}4c=oirD`$@JUVz{s-|JTpArUpuaHo##5S3_&beb;+`z1=wd2?VG6tZZG+
zXZ7h>AX<q)o8#7UKQEF5d}#GOQQQQ5;Xy!911TizHfd(2f+o=17L?evS1n;?1VHC8
zz6;GPrKQdtMQ;Fd)$%ufc3wx7KXuN;0Zee>NHr)6nv=oxUQziD41w<Bw&z=Gl&sGD
zVL<>6OPc{W^cGVU<6b7<6P6f9{}7NEEkOmM_!j2SsTRc+NF0~}QY_l2U@;(`R5$+B
zSs?0!r5*0YZKr3jDEemjKCIL}(Pn8YB{0fk3fcXSpK<f5udV91rE4~nsp%~m{`xj?
zH};T^$0;NY{`j%hlAaJ(6NoaUf7UD3u(I|L1MtCVC%KE?BWW5elC}eP(|As1mHc2t
zfzYIM{=rMpKyCWBU=JN)K?)BcsS+0<h662bnFzYN+m-1G$HbWox;0;rp9nnsJHx+I
zq%qf-?*zF|Q*GzBh7lGCqQsj!m^4l0tADr;M87jC73#gyi9)}zZw>27EjOD=u@`)|
z*`>0Aiz9t79$r`39;SWpxc~Q}Uz>E+W^a4WE(JC%+o&QTk~dCqvfX03>#LQ@rBU}Y
zd8Zr273D)^^9gBA4!HUULsw2+kk3;!COV(^RbRwhRw+)-9!rsvzpiW5{mG=1&Q{)y
zXB|V=dNl2%rS=>&-}JSYRyPZ_+L(=o3iO4W{#{y|g(wR&?eunLlvLOJqI=0c7*zGB
z%;i0FKJ>aGgEMV5NrD$u9vOZh5E%WST@v@)bVh)r;A~~>cdrb2Ko4K|IGnY8rzcL^
zYn!3xvlkZ?OU-^!PrFn)*@Q5ynU&tBmE=0hw_wvSE<SiY6UmMWd(&Qm$57^<s^7Cf
zN;)&^lX)Yy7=<ZbCUDYi!R5Ry|6YdRi0CPmQl}?3qfPeKzrM$p5E(@n(lnR(blBK;
zlo-1<ddVA(^bw>|b(@V9XPk0{Rw)`;a!bA=rvz7d0Tg`vK!RM@yWA(d^wVrlDAT!a
zuCp_3dQxdNI~o-X38Q!4!|+*`CODQl;7)jZ7@M$ED!mltc7@h22QxDtOt1(sEz1gh
zo{ahVq4lV7jbCB+ys!pdtd=@Si)fdUAiH~lx3h$Y1&h(gSxH;}@_F2Ac~z~bAbpH*
zuN6d_>W%sa3;pTDl0y&u2^f<0*FrwRX%SDre+D9=b}BNdLP~C_78xT8DIs_sFCjHQ
zQtYX~E{FKoV1NrqZ|E88{T#76&YjaZKaTpu?5=|Y(C_A&PKGXXjSen02M4+r(%gqd
zLB`Av17{HZ<vsUsV0)Y7Ic*mK-%Q9p0GusHn~aO-5bs#W3<BNws?VM+2TSMet}9Q$
zoC>~A$qg&q)LP~L)xX)BP1$jRy9Uc7_~-DRrU=okIKhVgh&sfyOl0K%bHrL%RXN1o
zy2Su2Iy_|&G-TGNC<Zqoeh&swK-cb-^NB%EvgHspD5cax9mG-5+|kR=V*#`6u^M4j
zA*+A7Z%8nmgU7_1?h`N$mYQN0ZOJFSi(9YbDX&oQ45poR@+Xghw4HaVw`I;7v<4;q
zTjhj1hL*2*+^c8$-{f#X^l_fRT9ATBTdo+l$~l}PzIz%)v^hl{9y+D`SJJQEf{QX&
zlc{!lbZOW^*A2nC%*qBl$7?%YiE(M{!0y>AD?yIU=FFI_MibDkgL4OI=Er>JKK;jk
zPc-I<Hnfo##b}PQnk9O2vgK2BunKbL4QvD$pWU?%dAeTa_eWryxuoEO7Zq8+&wqmi
z8=v03DOA~0ByDu?5eIW^9c*ySf5aD}W+iNNz!@t9HsFbVS>-Ik;v_J7v+P>AO`Q{x
z>pr?6%z3FrMmid_QM%Q0_$aX+o)0|BR)xAR%We6qP?xs#65jOwwIEwlH(g~&=c(Zs
z$<8P?T4p<6RgKJxA)H8|#xf4vSQb6UJ9|FnucQ`a2sX|Q^s}{fdw6_C2?qViKl{CT
z?Ri?maZ6-$jci{&X<Tq82IcwpK>{4p33+uMrUK=S8KU&z?94f!($?H|r_Kg8b-&GT
zK}>E6IrpjU$AYzg%J(NseD1q!E)6nZONEm^LNxvFtEBAMT`R+cTm)`N>Qd8w14bxP
zpjd6FlCc<g7t`Xoh0yw3Ki*r{kKw)e$Xn1(iLj%8m236WPE)+qmPE5D<aO>CB&R6Q
zM2ESQa8zpjEO)mq|IdhJ`|OX4lxY(bRbPVh1?|-*-|vWcg@!kHh_^5N{@gc!$N^J0
z@{rXIoT1cTr{C5Skp9=W%bA!U&cA@Hw}1uh-PDkU3iV{SN+1_>%|<C+v*i?@M>jme
z4L1Dev!FNLO(Ad(!ZoYDX%al6Frwg~=1%=Ruu$(LxBbrTbMJR`^jA$!02AKB)>--u
zKc^L6ZX&Y{^4jaPz@7fD-uLPLpDM09s)=v;TR@~&=@1dV6akSc2rEs4AQ+Jn5Tq#O
zNfkm(=$+67MHE&+ARqz~q?bsOA`p6$-eTxrAaq`Q@BRMx{V{jX?w&I<cka%dJD+p+
zW2u}+*^Mq-hU`7%V`r23WFG#{GPwREn}3&{w7Qa@kLMam)iqt1&FnqCepSsLYwFop
zj8P#kj%wFsQ~a6aT8FB#_PSVoSO!0@bkfvOZx8z@-)xRFBE}+>6Sr(<7O3ajtfdt9
zXWfb?4HFNMh3WVYxW#9b`6(88Sm7)p&0mEjvE$VRg5M0WOZtA}L(XqD2ll&tW1s6@
z-j!f1_C%hdy1&kUvr+afT*i?XU^iKNrAlCiIk^Owqb6sTixa8*xmun>)1PkEx$f#a
z)Z?jO@a2=6kT327sX|aBTg^qHt~dilx*?-RpLWHwKZB>^fL|t)rg$NyqpLsW5T<1U
z_*cs;)rlAnmn)DI7@#a@VrRGr-TUx>_iUu0caIEUoM7XNk{`Rr$5B?gBF2NMa+R!f
ztLDjXpb@pmXD>Jc0-~3}2q)xx(_^dDHyn%)3-TR1kF7t?s+(#g5ojw&B>clIJL<3$
zo!?KXwPXR>Fp|ir(S4Yf?O(y_z_M6X+Gj=wZPX)i-qOtMk7kXSw&A<2GT2jwMj@0!
zS@C3~;G-p`U`tpo&yO=pn6^uvr}0JP&W95D^GcR5-ep9Tda)zXu(Y)O*)k04R&V&8
zG2@mpZ`}Ic)o9kPlmza@*P9;?bjS=#uhkWUTIwbyyBK%dW~X0VJfj}7QUo}rGx?7F
z0mtH@E*}OrXY;xVbpCk;3b47<0X;4T-Ra{Xp%u#@_oPI<3Vi<$Qc1C#rb@t$JC3U&
zdH1dgmNmG@(EuU2syo+H)AV6<aE*<N85E3bb5Km9yxy}1ils@pd4?t1gx$3@Srk5M
zuWKqL(ku-#RIW(OSUvs+2>*0rXfMkZGJEnn$L$i91v64^vl=-4QY9KhYL;M{9=xfL
z_+;dVDA`IUaY&MXO9>f=qQWtoOhWV?*MaeOo(&~QRl6=e{!S-{Ml;)q^6u?hh5wzH
z9X|aXlQ_lkjwb-ce=f=;)-r@7!V&d9S%#BxTM-cv&ya_X49gx+D%<S0;C%b(O5Y!y
zYmg-pL*j})lI@_N026IxA}p0QIEdTRk}PvIEfH(8X8TO+Y%Wp)`>VlpawNBAK^`U;
z`rvTqeTR+;;D49jUR`v@@F7epfleVv4*}Dnz;gufURM_8x3`ueAZ+j>16iVSD-8^0
zb0QKyu+|x7(q6|&w_etg0BD=f6LlBaa>cT5{uP`x?BqztTl>c7b|>}12dLUlGUnX+
zw}O(ty-~C}gS*jkk!QnE)FI(wK{{_k)8~IpM9^OXg%ozNUR3bvdhRnLG8sNWJgGfR
z_8%lN2H5ZFziz8GY6QgzH3Ye?435Q`Rk_Z}FU&>hmEiQreUIg4{G|G1<$I2%d$9t;
z{qhS^KPaC>Pgn7fp-iB{sj{7skBHHaw{b2%mC?)vV!;PakXvhvC1iQBBX!j}Ltq}E
zGK-@o4-LAR$J<j1-l(tBNkhXP^&^?x82&i7_@|ez(iq9bVb7ZCf0I<L>?}rfeq4GV
zj>--8T1aSS*v<3^4(EO#NCw`uuU>H!cI<iY?X(qX+A)3niR`Ek@vl`T(xwf|-{X9)
z3#-c{gKMh0Ua6%h86EUZJHrt^sdwDh$EV|(1Q0Ngw)ACgXD`c2nFu4N3E^y^8n-cZ
zu9U!|@>Rb-KOms8{8l)_rp)6FMLQ<Ffjap0l~}1CxLnL;*VbWX!<EOxP^m8JDqEq&
zp}`g5-Bc|h)n9Te75mre%Wbz@ZR~!oJNt4GfsWp32Dt|HqOeZPFj{<5pLU6~w)FrR
zhE3PkDkObZiUAD=jH<8c-vt~u$D2I_tP_ID#%AB-gIP78&z?gv@nmUQejs9G-HDiS
zC$8*NyTS2fa~JH$X8<Nqu%T4=m~!u+3^MSw3y{*lV;2-0m^*y#QSvgl@|y_$?NK6o
zLR8bVVM7<~nKP*DfMf$6<XZ&JX}zJNvYR0rGxrwNg+vQig7xW&*IfUj#5`TT0VnRi
zkeA2$NFVl}Mr6>})9)lwkfNs!jL4<IN@z2G$xLyr=^&UX@TxwZw5s`uTyfCqv)de1
zRyrG|w}9EXLr0dnHzCBSgcMLi5h#zY$1i-F`QSZ1x^EC-#ptDhJQcB5b;7ByVuK{E
z9z9ir#t9FkcAWpe8u-6D*s^c1t6=av<73%}B8R@_-|BsX$>b@)ef?neXC)kFvTuXq
zQZEIL_3xLPZqyz)Rv~EJ8}GmuhI#R6%_&9v>$2%oJw5>Cu;RB5=3}K60iL{JCUyB0
z^-U2gZyZ+P$N`;x_ZLEyDdDLZ>}L(jq~~b!PZ*y3c;?zGvIGYB8!{t;hLFvVtc_x?
zTg|9)dzHVG^nK*bCLIH!$O9;59<dG304)BTzFDSD>QSirj(aA$^sb8&h)YA2dAE!u
z##8^~;+rXgQ*#Ph2XF3Z`BY=S;q<8zy>6FD`dgI?w2*hCNnW&KKEv{U0P9Rz_1JZ@
zij-yNNi~))ho|<>9%%mv7)<<eYl>kjMP*ue`(`fWVQWtl;HlO~bn=KoYLMQ&&(Kg0
z_!yZh7Fd`8ARDFm*DNGkcQbwAMI&VzGbfSFOPt7?Z+{-$^P4PiCmo#YmF)9%X}=ao
z_Rq#RE0=x2*v`B3*XkYzTi`EB(-~*+4Dd;v{4Q2f2%)42-sViK?Lo<V;yvJvUB^Jw
z{>=~-@tP9Wf6}@I0cWQ#o}0~<u#JNI9HyZ}_y$*4;=rA=<H|=Vo>vqzM~SW6PpwDl
zlu2InVsA)8Fs4LLB__RMM>z8gl3vi6%ZA?^)~~aY#Pm%AXy^B?FiA4Wpd^aF>9K}4
z)Gh&JSFFr6a4iud_wZL`^fBGmJ8B-4eSjQ&2v*tom0C6AD-|*VQ_Zp2V&cErMUo%G
zT>9npkaXmT*n;*~QV;u4(B;8eAdYl*Q-b8R378Wb=`H>QaT0`LR2E)7t0BkduK;z(
z<8oKPf0Jl1L&=1wKk&7~Ia&p>{wr_f-i^Wdq}4uoW`uzxgZVT;O2s25EKpEp^S6w*
zG}T|j^6S6}8T@u#WoE>v8@M>Ue~=?Tr0@VAvivu#c7z2TtO~QzO*gKtlGG})?^ye5
z!@dl$M#Tp@?(dvvCH}dly8kMOisZ#8=3`N#4KsBLnjiGAfGt;<Zq#REkR(6uuDU5<
z!OgO~+6qtiCOhC>X-!)_tlT`6hnOMqi?oR7fN0hKIcWIPT=Yy;{%}NmFWu_3)K+9e
zIm(rnu4Ui)YT*-{seO*o<Rjxp1P%^a<z2n;NV={etNZJ2#`Rz4jV-t0d}3Js$>F^1
zej!XwJ>y<Ky~KW~c8ZgtJ;MHXJ?pWC?S5lfQf#5@$is;W{~IG<CMlEr7P6`G0u)r=
zAb%A&ke7GvKz*AzDRda0Cjy;C2B(&tGd9T$8S5P*2HDOF(i=U78S^UsdwIpe(X?Pm
z(5Jabj3oR9Skh%o8T8Cr1YkQnOa6_vYu$CzMs#&b@S%4BZx>Wl){>~RWcXGNj()07
zBm^o^njt=HOG)P$bGvMj<cjmKZK9a^6=q&$Vb=)c><jl>d*c+WWGuPNdFSl4x8+xL
zcp^FhLldmFb@n6>C%(Gt7n#NF*9>(L8<Gc?m$xqMn6K18NI(6Wqt@!3u_hzi+tw|d
zJ;Tc$Io7QS3ogMWxsHeTTG@Rdl@F2D-rFtO*a(+1e)nzP_>f0dFWY6T^3>#nZ=kut
z)FE;gU}G`shnm1I^gZmA7Pt7ltJbqd>3+in(Tx7aPtEFCqIDN#d<-hZ9RtSqZYE0W
zrSG)Oz!UzOa&7J0BXk|KJx$rp<TblzUg(}l?jw5GH5BCh#H<OZILodWf?EZ+ppj3F
z`Ld*rm)-sgJk{%q8~wZQJ5RbCHfW3#C*-$`m1Oq&MkA18$(T!b;G5_^*0JO)ip7rG
zcRoymwD`c?KSxfb)Uppran-CC+u8U08zmt#3Riewn=7aJopUGMtk(IWwE?XmLTMPt
z+LoX4Pr(MPze3h}2%!KTl)|yv(N<p5h4Sk8*KG72gBoq@6+Ph<TWyCKLaUJ%m6Bcb
z)6`;YBJ=u5YdP-F|4$e~fLrYF-ek<+S9Mq*5_jR0Ci&Vzr`I<1)(!rxqc*q3fWrYM
zEq|TXnSp^Esuw$}=%>ZL&Fs)xT~oDf6^e~+XIL++p#9)(d=N!F=6~wVw_BAx_&xYM
zR{Bli(eYv!jKv3?LG;GH@}?6?4Z|MM5nnO4e0?m|Ff|ppXNOl`h~0Y`9<}zOE!Aj*
z8!Ockwf5~lt1X0j#{ZrDQ`vm1a`_$sZMeNfb-$~LDQYdm5jrM_r%gI^K$dRu69TI-
z=wO#is<KDYNpM!PudKi9U_%Sozl$8C|As=O<E#brlBwV6j8@u*3LM<36NM>P#y$!C
zvM@Lb4hjA^ulC=Vi>=|2M;jA})?m0R-%RYofWe(Ti~1QR7&v(?XU8s+Y;gC?R%WeL
z!L>0aCR(Qj>M}~d;xTS}3yP{9-s(B?AS5C))jzw<uKzlJ;i8gV`_oj+rP`u?-;Mp3
zghY>Xuz2>9Yi_NyN9EH&)Gi1MFSY0_FFx!izuUgbuHAo*aZM2?8NW)Y;DvF7;0gPT
z)GrOtDGDyQaakF!Z5IqZdTrC?yDf!=PUUtU>QV>DFU}J`2iS)bV#-P^U&lkuw)iEh
zJx%0zH!07(OgPjkDkUX8aWqH^a_O0`qybvz(T)Fl9gz+95>m9-xXz{asX^#z@t&B%
z{R@%^DK8c2#ZPvqYm}3`&Aku8hiiIx&?CbUw|(_|rY0bxz4;NIaN1dk0+Rt25q8~m
z*ViJQtoSBA)twfjbs%e9$lqT)<eZhbk5$?w_r(OUqyiIhUzPbzgoR-FeNv^|$ADN8
zCW68JaT?pVuWVeeuTq$B){62}3MKa@;t8~dL*<U^=SI2GG<KCMB(i&JXf@Zz8@@O=
zPoTvlGa%r*cpLc$M=yB7bWjxUoVqvym%Znu#CIdTpoP(Bu@@;M5v_cqHMNj7(k>&^
zaoeLrvAtpL?WEgn*i$yTe!@3Uh2%b#+#yJVu`j_jm1Be;YHg#eOApsVI9VXAZQiuf
zQjG!nr7OiW#YrbqM16}jO*4f#mFiQ0g~HyXKmiNHM;o=_>8J>3y5;%{n9VSp1?>LF
zgGZ%`6cZFqid;l3P4&d(Z}&H~Cb<+$+o;LNa1K$8fH5@2JX^Quq!5#7rJ4{3tmwEV
zK2z_8-hwhE=8SWTiEHEBW(ZMiu&^-CBVGwqzMSjS3(Z$KPc(G1SVna_?{7>PF}8x;
zX%S)DLyHw*U&C+63JLe^N^!sxE~uIJUl|%nyrfmQ?Wl5?6Zk4Z{J!5sHP)mn_;^da
z%yM-NVAE~vpqVN5N5VmE32PlNu~4`d<!6dbD=x*gBan7yoMq-{5G`;Hyl(D(*HG7$
z6|ReGQ2b19<UN<FYAydG1k1Pcz}9cxYgT39tyDB`a!))Kd4E~=tsLa`(9Ahl>+@6!
zEz}v>@xJIbCCU^mG!u~I#s{-)bvxh>^jQ2rmeR~SkmWUhv##IcMpH`iwzIke(?_Es
zdNL>{2Btgr^8mJm7JfEgXvW=CwO68BUfrM>>e>dsTEA+sU@z~3hra8yg%2$9j<+d4
zkbW;mO*CJ|!8aP&dGI2y^;7HWUmQF?t8MKo25|IdYyi|1rFDC+_`C6l-eWv)K23k*
zgQa)TnTT@DBu&*11EmW_9GSS;h?`#--<@+s<yYm1!QP_!=zy5RBh@Fi8&RULnA>01
zQ^XEJcaB9*n88q~y9%;yjAx;+>%Ivro$MnB9pjDf3D`QvDRZ0ts(pQk!wwbv)MBb|
z3~@&P1p)cU7$1flxH_?NzED{6Y%Np(;rvmhZ7@d<A>Fj3@QA;cX5cC+1+^i6HTzca
zXSxEmInboh<h`R!JgRw;@d3!<-3J~)38pUx6v~k2u0RCHxilf7c;sh3I<&(|4{rwZ
zb+U%$+?I?Q%^m*|^!_hw_4y9&`c8IbHHGTQi;5omnBGtrh!6RRaM#WW?i0n&{RXqo
z_oa%Jwo;gJ!Y^$lmb`&bhqJ`HEmLl}<3(ZT>b#`>a-iPqm_hQYqJ@5t?DS#(f51=V
zg$qY%a-+kb&10G*Yxnre8M@R5(N&645&i7VziDkr5jT-q+<;I#$WjTh!T~K%S3=-Z
znc2;eUfVL%;o8A571bnRjraisCP!cz8XF2pjY!U_gYvA95GCJt=~MDMj;<jg?^G9l
zAx`+Ou-qK!osti(<-6Jh_-=rBJR2+1<(L>b-#y`7<8!QlfV-8bxVWR=<L7@p*S+`f
KZi$vn*uMcj{I1LZ

literal 0
HcmV?d00001

diff --git a/packages/context-coloring/scripts/download-dependencies.el b/packages/context-coloring/scripts/download-dependencies.el
new file mode 100644
index 0000000..37f243b
--- /dev/null
+++ b/packages/context-coloring/scripts/download-dependencies.el
@@ -0,0 +1,40 @@
+;; -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2014-2015  Free Software Foundation, Inc.
+
+;; This file is part of GNU Emacs.
+
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+(defconst directory (file-name-directory (or load-file-name buffer-file-name)))
+
+(defun resolve-path (path)
+  (expand-file-name path directory))
+
+(defun strip-headers ()
+  (goto-char 1)
+  (kill-paragraph 1) ; The headers are 1 paragraph. I hope.
+  (kill-line)        ; A line separates the headers from the file's content.
+  )
+
+(let ((files '("https://raw.githubusercontent.com/mooz/js2-mode/master/js2-mode.el"
+               "https://raw.githubusercontent.com/rejeep/ert-async.el/master/ert-async.el")))
+  (make-directory (resolve-path "../libraries") t)
+  (dolist (file files)
+    (let* ((basename (file-name-nondirectory file))
+           (destination (resolve-path (concat "../libraries/" basename))))
+      (when (null (file-exists-p destination))
+        (with-current-buffer (url-retrieve-synchronously file)
+          (strip-headers)
+          (write-file destination))))))
diff --git a/packages/context-coloring/test/context-coloring-test.el b/packages/context-coloring/test/context-coloring-test.el
new file mode 100644
index 0000000..607882b
--- /dev/null
+++ b/packages/context-coloring/test/context-coloring-test.el
@@ -0,0 +1,375 @@
+;;; test/context-coloring-test.el --- Tests for context coloring. -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2014-2015  Free Software Foundation, Inc.
+
+;; This file is part of GNU Emacs.
+
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+;;; Code:
+
+;;; Test running utilities
+
+(defconst context-coloring-test-path
+  (file-name-directory (or load-file-name buffer-file-name))
+  "This file's directory.")
+
+(defun context-coloring-test-read-file (path)
+  "Read a file's contents into a string."
+  (with-temp-buffer
+    (insert-file-contents (expand-file-name path context-coloring-test-path))
+    (buffer-string)))
+
+(defun context-coloring-test-setup ()
+  "Preparation code to run before all tests."
+  (setq context-coloring-comments-and-strings nil))
+
+(defun context-coloring-test-cleanup ()
+  "Cleanup code to run after all tests."
+  (setq context-coloring-comments-and-strings t)
+  (setq context-coloring-after-colorize-hook nil)
+  (setq context-coloring-js-block-scopes nil)
+  (context-coloring-set-colors-default))
+
+(defmacro context-coloring-test-with-fixture (fixture &rest body)
+  "Evaluate BODY in a temporary buffer with the relative
+FIXTURE."
+  `(with-temp-buffer
+     (unwind-protect
+         (progn
+           (context-coloring-test-setup)
+           (insert (context-coloring-test-read-file ,fixture))
+           ,@body)
+       (context-coloring-test-cleanup))))
+
+(defun context-coloring-test-with-temp-buffer-async (callback)
+  "Create a temporary buffer, and evaluate CALLBACK there.  A
+teardown callback is passed to CALLBACK for it to invoke when it
+is done."
+  (let ((temp-buffer (make-symbol "temp-buffer")))
+    (let ((previous-buffer (current-buffer))
+          (temp-buffer (generate-new-buffer " *temp*")))
+      (set-buffer temp-buffer)
+      (funcall
+       callback
+       (lambda ()
+         (and (buffer-name temp-buffer)
+              (kill-buffer temp-buffer))
+         (set-buffer previous-buffer))))))
+
+(defun context-coloring-test-with-fixture-async (fixture callback &optional setup)
+  "Evaluate CALLBACK in a temporary buffer with the relative
+FIXTURE.  A teardown callback is passed to CALLBACK for it to
+invoke when it is done.  An optional SETUP callback can be passed
+to run arbitrary code before the mode is invoked."
+  (context-coloring-test-with-temp-buffer-async
+   (lambda (done-with-temp-buffer)
+     (context-coloring-test-setup)
+     (if setup (funcall setup))
+     (insert (context-coloring-test-read-file fixture))
+     (funcall
+      callback
+      (lambda ()
+        (context-coloring-test-cleanup)
+        (funcall done-with-temp-buffer))))))
+
+
+;;; Test defining utilities
+
+(defun context-coloring-test-js-mode (fixture callback &optional setup)
+  "Use FIXTURE as the subject matter for test logic in CALLBACK.
+Optionally, provide setup code to run before the mode is
+instantiated in SETUP."
+  (context-coloring-test-with-fixture-async
+   fixture
+   (lambda (done-with-test)
+     (js-mode)
+     (context-coloring-mode)
+     (context-coloring-colorize
+      (lambda ()
+        (funcall callback done-with-test))))
+   setup))
+
+(defmacro context-coloring-test-js2-mode (fixture &rest body)
+  "Use FIXTURE as the subject matter for test logic in BODY."
+  `(context-coloring-test-with-fixture
+    ,fixture
+    (require 'js2-mode)
+    (setq js2-mode-show-parse-errors nil)
+    (setq js2-mode-show-strict-warnings nil)
+    (js2-mode)
+    (context-coloring-mode)
+    ,@body))
+
+(defmacro context-coloring-test-deftest-js-mode (name)
+  "Define an asynchronous test for `js-mode' in the typical
+format."
+  (let ((test-name (intern (format "context-coloring-test-js-mode-%s" name)))
+        (fixture (format "./fixtures/%s.js" name))
+        (function-name (intern-soft (format "context-coloring-test-js-%s" name))))
+    `(ert-deftest-async ,test-name (done)
+                        (context-coloring-test-js-mode
+                         ,fixture
+                         (lambda (teardown)
+                           (unwind-protect
+                               (,function-name)
+                             (funcall teardown))
+                           (funcall done))))))
+
+(defmacro context-coloring-test-deftest-js2-mode (name)
+    "Define a test for `js2-mode' in the typical format."
+  (let ((test-name (intern (format "context-coloring-test-js2-mode-%s" name)))
+        (fixture (format "./fixtures/%s.js" name))
+        (function-name (intern-soft (format "context-coloring-test-js-%s" name))))
+    `(ert-deftest ,test-name ()
+       (context-coloring-test-js2-mode
+        ,fixture
+        (,function-name)))))
+
+
+;;; Assertion functions
+
+(defmacro context-coloring-test-assert-region (&rest body)
+  "Skeleton for asserting something about the face of points in a
+region.  Provides the free variables `i', `length', `point',
+`face' and `actual-level'."
+  `(let ((i 0)
+         (length (- end start)))
+     (while (< i length)
+       (let* ((point (+ i start))
+              (face (get-text-property point 'face))
+              actual-level)
+         ,@body)
+       (setq i (+ i 1)))))
+
+(defconst context-coloring-test-level-regexp
+  "context-coloring-level-\\([[:digit:]]+\\)-face"
+  "Regular expression for extracting a level from a face.")
+
+(defun context-coloring-test-assert-region-level (start end level)
+  "Assert that all points in the range [START, END) are of level
+LEVEL."
+  (context-coloring-test-assert-region
+   (when (not (when face
+                (let* ((face-string (symbol-name face))
+                       (matches (string-match
+                                 context-coloring-test-level-regexp
+                                 face-string)))
+                  (when matches
+                    (setq actual-level (string-to-number
+                                        (substring face-string
+                                                   (match-beginning 1)
+                                                   (match-end 1))))
+                    (= level actual-level)))))
+     (ert-fail (format (concat "Expected level in region [%s, %s), "
+                               "which is \"%s\", to be %s; "
+                               "but at point %s, it was %s")
+                       start end
+                       (buffer-substring-no-properties start end) level
+                       point actual-level)))))
+
+(defun context-coloring-test-assert-region-face (start end expected-face)
+  "Assert that all points in the range [START, END) have the face
+EXPECTED-FACE."
+  (context-coloring-test-assert-region
+   (when (not (eq face expected-face))
+     (ert-fail (format (concat "Expected face in region [%s, %s), "
+                               "which is \"%s\", to be %s; "
+                               "but at point %s, it was %s")
+                       start end
+                       (buffer-substring-no-properties start end) expected-face
+                       point face)))))
+
+(defun context-coloring-test-assert-region-comment-delimiter (start end)
+  "Assert that all points in the range [START, END) have
+`font-lock-comment-delimiter-face'."
+  (context-coloring-test-assert-region-face
+   start end 'font-lock-comment-delimiter-face))
+
+(defun context-coloring-test-assert-region-comment (start end)
+    "Assert that all points in the range [START, END) have
+`font-lock-comment-face'."
+  (context-coloring-test-assert-region-face
+   start end 'font-lock-comment-face))
+
+(defun context-coloring-test-assert-region-string (start end)
+    "Assert that all points in the range [START, END) have
+`font-lock-string-face'."
+  (context-coloring-test-assert-region-face
+   start end 'font-lock-string-face))
+
+(defun context-coloring-test-assert-message (expected)
+  "Assert that the *Messages* buffer has message EXPECTED."
+  (with-current-buffer "*Messages*"
+    (let ((messages (split-string
+                     (buffer-substring-no-properties
+                      (point-min)
+                      (point-max))
+                     "\n")))
+      (let ((message (car (nthcdr (- (length messages) 2) messages))))
+        (should (equal message expected))))))
+
+(defun context-coloring-test-assert-face (level foreground)
+  "Assert that a face for LEVEL exists and that its `:foreground'
+is FOREGROUND."
+  (let* ((face (context-coloring-face-symbol level))
+         actual-foreground)
+    (when (not face)
+      (ert-fail (format (concat "Expected face for level `%s' to exist; "
+                                "but it didn't")
+                        level)))
+    (setq actual-foreground (face-attribute face :foreground))
+    (when (not (string-equal foreground actual-foreground))
+      (ert-fail (format (concat "Expected face for level `%s' "
+                                "to have foreground `%s'; but it was `%s'")
+                        level
+                        foreground actual-foreground)))))
+
+
+;;; The tests
+
+(ert-deftest context-coloring-test-unsupported-mode ()
+  (context-coloring-test-with-fixture
+   "./fixtures/function-scopes.js"
+   (context-coloring-mode)
+   (context-coloring-test-assert-message
+    "Context coloring is not available for this major mode")))
+
+(ert-deftest context-coloring-test-set-colors ()
+  ;; This test has an irreversible side-effect in that it defines faces beyond
+  ;; 7.  Faces 0 through 7 are reset to their default states, so it might not
+  ;; matter, but be aware anyway.
+  (context-coloring-set-colors
+   "#000000"
+   "#111111"
+   "#222222"
+   "#333333"
+   "#444444"
+   "#555555"
+   "#666666"
+   "#777777"
+   "#888888"
+   "#999999")
+  (context-coloring-test-assert-face 0 "#000000")
+  (context-coloring-test-assert-face 1 "#111111")
+  (context-coloring-test-assert-face 2 "#222222")
+  (context-coloring-test-assert-face 3 "#333333")
+  (context-coloring-test-assert-face 4 "#444444")
+  (context-coloring-test-assert-face 5 "#555555")
+  (context-coloring-test-assert-face 6 "#666666")
+  (context-coloring-test-assert-face 7 "#777777")
+  (context-coloring-test-assert-face 8 "#888888")
+  (context-coloring-test-assert-face 9 "#999999"))
+
+(defun context-coloring-test-js-function-scopes ()
+  (context-coloring-test-assert-region-level 1 9 0)
+  (context-coloring-test-assert-region-level 9 23 1)
+  (context-coloring-test-assert-region-level 23 25 0)
+  (context-coloring-test-assert-region-level 25 34 1)
+  (context-coloring-test-assert-region-level 34 35 0)
+  (context-coloring-test-assert-region-level 35 52 1)
+  (context-coloring-test-assert-region-level 52 66 2)
+  (context-coloring-test-assert-region-level 66 72 1)
+  (context-coloring-test-assert-region-level 72 81 2)
+  (context-coloring-test-assert-region-level 81 82 1)
+  (context-coloring-test-assert-region-level 82 87 2)
+  (context-coloring-test-assert-region-level 87 89 1))
+
+(context-coloring-test-deftest-js-mode function-scopes)
+(context-coloring-test-deftest-js2-mode function-scopes)
+
+(defun context-coloring-test-js-global ()
+  (context-coloring-test-assert-region-level 20 28 1)
+  (context-coloring-test-assert-region-level 28 35 0)
+  (context-coloring-test-assert-region-level 35 41 1))
+
+(context-coloring-test-deftest-js-mode global)
+(context-coloring-test-deftest-js2-mode global)
+
+(defun context-coloring-test-js-block-scopes ()
+  (context-coloring-test-assert-region-level 20 64 1)
+   (setq context-coloring-js-block-scopes t)
+   (context-coloring-colorize)
+   (context-coloring-test-assert-region-level 20 27 1)
+   (context-coloring-test-assert-region-level 27 41 2)
+   (context-coloring-test-assert-region-level 41 42 1)
+   (context-coloring-test-assert-region-level 42 64 2))
+
+(context-coloring-test-deftest-js2-mode block-scopes)
+
+(defun context-coloring-test-js-catch ()
+  (context-coloring-test-assert-region-level 20 27 1)
+  (context-coloring-test-assert-region-level 27 51 2)
+  (context-coloring-test-assert-region-level 51 52 1)
+  (context-coloring-test-assert-region-level 52 73 2)
+  (context-coloring-test-assert-region-level 73 101 3)
+  (context-coloring-test-assert-region-level 101 102 1)
+  (context-coloring-test-assert-region-level 102 117 3)
+  (context-coloring-test-assert-region-level 117 123 2))
+
+(context-coloring-test-deftest-js-mode catch)
+(context-coloring-test-deftest-js2-mode catch)
+
+(defun context-coloring-test-js-key-names ()
+  (context-coloring-test-assert-region-level 20 63 1))
+
+(context-coloring-test-deftest-js-mode key-names)
+(context-coloring-test-deftest-js2-mode key-names)
+
+(defun context-coloring-test-js-property-lookup ()
+  (context-coloring-test-assert-region-level 20 26 0)
+  (context-coloring-test-assert-region-level 26 38 1)
+  (context-coloring-test-assert-region-level 38 44 0)
+  (context-coloring-test-assert-region-level 44 52 1)
+  (context-coloring-test-assert-region-level 57 63 0)
+  (context-coloring-test-assert-region-level 63 74 1))
+
+(context-coloring-test-deftest-js-mode property-lookup)
+(context-coloring-test-deftest-js2-mode property-lookup)
+
+(defun context-coloring-test-js-key-values ()
+  (context-coloring-test-assert-region-level 78 79 1))
+
+(context-coloring-test-deftest-js-mode key-values)
+(context-coloring-test-deftest-js2-mode key-values)
+
+(defun context-coloring-test-js-comments-and-strings ()
+  (context-coloring-test-assert-region-comment-delimiter 1 4)
+  (context-coloring-test-assert-region-comment 4 8)
+  (context-coloring-test-assert-region-comment-delimiter 9 12)
+  (context-coloring-test-assert-region-comment 12 19)
+  (context-coloring-test-assert-region-string 20 32)
+  (context-coloring-test-assert-region-level 32 33 0))
+
+(ert-deftest-async context-coloring-test-js-mode-comments-and-strings (done)
+  (context-coloring-test-js-mode
+   "./fixtures/comments-and-strings.js"
+   (lambda (teardown)
+     (unwind-protect
+         (context-coloring-test-js-comments-and-strings)
+       (funcall teardown))
+     (funcall done))
+   (lambda ()
+     (setq context-coloring-comments-and-strings t))))
+
+(ert-deftest context-coloring-test-js2-mode-comments-and-strings ()
+  (context-coloring-test-js2-mode
+   "./fixtures/comments-and-strings.js"
+   (setq context-coloring-comments-and-strings t)
+   (context-coloring-colorize)
+   (context-coloring-test-js-comments-and-strings)))
+
+(provide 'context-coloring-test)
+
+;;; context-coloring-test.el ends here
diff --git a/packages/context-coloring/test/fixtures/block-scopes.js b/packages/context-coloring/test/fixtures/block-scopes.js
new file mode 100644
index 0000000..735ca6f
--- /dev/null
+++ b/packages/context-coloring/test/fixtures/block-scopes.js
@@ -0,0 +1,6 @@
+(function () {
+    if (1) {
+        var a;
+        let b;
+    }
+}());
diff --git a/packages/context-coloring/test/fixtures/catch.js b/packages/context-coloring/test/fixtures/catch.js
new file mode 100644
index 0000000..a542ce9
--- /dev/null
+++ b/packages/context-coloring/test/fixtures/catch.js
@@ -0,0 +1,8 @@
+(function () {
+    try {} catch (e) {
+        var a = e;
+        try {} catch (e) {
+            var a = e;
+        }
+    }
+}());
diff --git a/packages/context-coloring/test/fixtures/comments-and-strings.js b/packages/context-coloring/test/fixtures/comments-and-strings.js
new file mode 100644
index 0000000..9910b02
--- /dev/null
+++ b/packages/context-coloring/test/fixtures/comments-and-strings.js
@@ -0,0 +1,3 @@
+// Foo.
+/* Bar. */
+'use strict';
diff --git a/packages/context-coloring/test/fixtures/function-scopes.js b/packages/context-coloring/test/fixtures/function-scopes.js
new file mode 100644
index 0000000..2f6ed32
--- /dev/null
+++ b/packages/context-coloring/test/fixtures/function-scopes.js
@@ -0,0 +1,5 @@
+var a = function () {};
+function b() {
+    var c = function () {};
+    function d() {}
+}
diff --git a/packages/context-coloring/test/fixtures/global.js b/packages/context-coloring/test/fixtures/global.js
new file mode 100644
index 0000000..a35619d
--- /dev/null
+++ b/packages/context-coloring/test/fixtures/global.js
@@ -0,0 +1,3 @@
+(function () {
+    var a = require('a');
+}());
diff --git a/packages/context-coloring/test/fixtures/key-names.js b/packages/context-coloring/test/fixtures/key-names.js
new file mode 100644
index 0000000..d8ad17c
--- /dev/null
+++ b/packages/context-coloring/test/fixtures/key-names.js
@@ -0,0 +1,6 @@
+(function () {
+    return {
+        a: 0,
+        b : 2
+    };
+}());
diff --git a/packages/context-coloring/test/fixtures/key-values.js b/packages/context-coloring/test/fixtures/key-values.js
new file mode 100644
index 0000000..41bdedd
--- /dev/null
+++ b/packages/context-coloring/test/fixtures/key-values.js
@@ -0,0 +1,8 @@
+(function () {
+    var a;
+    (function () {
+        return {
+            b: a
+        };
+    }());
+}());
diff --git a/packages/context-coloring/test/fixtures/property-lookup.js b/packages/context-coloring/test/fixtures/property-lookup.js
new file mode 100644
index 0000000..4edcb41
--- /dev/null
+++ b/packages/context-coloring/test/fixtures/property-lookup.js
@@ -0,0 +1,5 @@
+(function () {
+    window.foo();
+    window. bar();
+    window.foo.bar();
+}());
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* Re: Requesting review before pushing patch to ELPA
  2015-02-05  8:05               ` Jackson Hamilton
@ 2015-02-05 14:10                 ` Stefan Monnier
  2015-02-05 18:17                   ` Jackson Hamilton
  0 siblings, 1 reply; 20+ messages in thread
From: Stefan Monnier @ 2015-02-05 14:10 UTC (permalink / raw)
  To: Jackson Hamilton; +Cc: emacs-devel

> The attached patches are the current representation of what I intend to
> merge. (Same subtree strategy as before.)

Looks good to me,


        Stefan


PS: Regarding "eval: (when (fboundp 'rainbow-mode) (rainbow-mode 1))",
this is usually considered a misfeature: the file does not need or make
special use of rainbow-mode.  Instead, *you* just happen to like using
rainbow-mode when editing this file.  So this customization should be in
your ~/.emacs rather than in the file itself.



^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: Requesting review before pushing patch to ELPA
  2015-02-05 14:10                 ` Stefan Monnier
@ 2015-02-05 18:17                   ` Jackson Hamilton
  2015-02-05 18:39                     ` Matthew Carter
                                       ` (2 more replies)
  0 siblings, 3 replies; 20+ messages in thread
From: Jackson Hamilton @ 2015-02-05 18:17 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: emacs-devel

[-- Attachment #1: Type: text/plain, Size: 1414 bytes --]

Heys guys, I am having an issue pushing to ELPA:

$ git push origin master
Counting objects: 1127, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (431/431), done.
fatal: read error: Connection reset by peer
error: pack-objects died of signal 13
error: failed to push some refs to 'git://git.sv.gnu.org/emacs/elpa'

I am not sure what's up.

I looked up these errors, and apparently "error: pack-objects died of
signal 13" has something to do with the size of the push? The push does
include my whole project history, but I don't have any particularly large
binary files in my repo besides maybe 6 or 7 .png files.

I've added the "Project Members GPG Keyring" with gpg --import. Perhaps I
am still not properly authenticated? (Sorry, I'm not too familiar with gpg
or keys.)

Thanks,
Jackson

On Thu, Feb 5, 2015 at 6:10 AM, Stefan Monnier <monnier@iro.umontreal.ca>
wrote:

> > The attached patches are the current representation of what I intend to
> > merge. (Same subtree strategy as before.)
>
> Looks good to me,
>
>
>         Stefan
>
>
> PS: Regarding "eval: (when (fboundp 'rainbow-mode) (rainbow-mode 1))",
> this is usually considered a misfeature: the file does not need or make
> special use of rainbow-mode.  Instead, *you* just happen to like using
> rainbow-mode when editing this file.  So this customization should be in
> your ~/.emacs rather than in the file itself.
>

[-- Attachment #2: Type: text/html, Size: 2096 bytes --]

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: Requesting review before pushing patch to ELPA
  2015-02-05 18:17                   ` Jackson Hamilton
@ 2015-02-05 18:39                     ` Matthew Carter
  2015-02-05 18:48                     ` Jackson Hamilton
  2015-02-05 20:22                     ` Stefan Monnier
  2 siblings, 0 replies; 20+ messages in thread
From: Matthew Carter @ 2015-02-05 18:39 UTC (permalink / raw)
  To: Jackson Hamilton; +Cc: Stefan Monnier, emacs-devel

Jackson Hamilton <jackson@jacksonrayhamilton.com> writes:

> Heys guys, I am having an issue pushing to ELPA:
>
> $ git push origin master
> Counting objects: 1127, done.
> Delta compression using up to 4 threads.
> Compressing objects: 100% (431/431), done.
> fatal: read error: Connection reset by peer
> error: pack-objects died of signal 13
> error: failed to push some refs to 'git://git.sv.gnu.org/emacs/elpa'
>
> I am not sure what's up.
>
> I looked up these errors, and apparently "error: pack-objects died of
> signal 13" has something to do with the size of the push? The push does
> include my whole project history, but I don't have any particularly large
> binary files in my repo besides maybe 6 or 7 .png files.
>
> I've added the "Project Members GPG Keyring" with gpg --import. Perhaps I
> am still not properly authenticated? (Sorry, I'm not too familiar with gpg
> or keys.)
>
> Thanks,
> Jackson
>
> On Thu, Feb 5, 2015 at 6:10 AM, Stefan Monnier <monnier@iro.umontreal.ca>
> wrote:
>

Hi Jackson,

When you issue 'git remote -v' do you see something similar to:

origin  jackson@git.sv.gnu.org:/srv/git/emacs/elpa.git

Or do you see an URL without your Savannah username included?

The default checkout didn't seem pushable to me (I had your same issue).

I fixed by setting the remote to what you see above (well, in my case I
just re-cloned via):

git clone 'jackson@git.sv.gnu.org:/srv/git/emacs/elpa.git'

Obviously make sure to add your SSH keys to Savannah first (and in the
sample commands change 'jackson' to your Savannah user').

-- 
Matthew Carter (m@ahungry.com)
http://ahungry.com



^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: Requesting review before pushing patch to ELPA
  2015-02-05 18:17                   ` Jackson Hamilton
  2015-02-05 18:39                     ` Matthew Carter
@ 2015-02-05 18:48                     ` Jackson Hamilton
  2015-02-05 20:22                     ` Stefan Monnier
  2 siblings, 0 replies; 20+ messages in thread
From: Jackson Hamilton @ 2015-02-05 18:48 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: emacs-devel

[-- Attachment #1: Type: text/plain, Size: 1697 bytes --]

I figured out the issue: I had to use the git+ssh url, and I added my ssh
key to my savannah account. Then git push worked.

On Thu, Feb 5, 2015 at 10:17 AM, Jackson Hamilton <
jackson@jacksonrayhamilton.com> wrote:

> Heys guys, I am having an issue pushing to ELPA:
>
> $ git push origin master
> Counting objects: 1127, done.
> Delta compression using up to 4 threads.
> Compressing objects: 100% (431/431), done.
> fatal: read error: Connection reset by peer
> error: pack-objects died of signal 13
> error: failed to push some refs to 'git://git.sv.gnu.org/emacs/elpa'
>
> I am not sure what's up.
>
> I looked up these errors, and apparently "error: pack-objects died of
> signal 13" has something to do with the size of the push? The push does
> include my whole project history, but I don't have any particularly large
> binary files in my repo besides maybe 6 or 7 .png files.
>
> I've added the "Project Members GPG Keyring" with gpg --import. Perhaps I
> am still not properly authenticated? (Sorry, I'm not too familiar with gpg
> or keys.)
>
> Thanks,
> Jackson
>
> On Thu, Feb 5, 2015 at 6:10 AM, Stefan Monnier <monnier@iro.umontreal.ca>
> wrote:
>
>> > The attached patches are the current representation of what I intend to
>> > merge. (Same subtree strategy as before.)
>>
>> Looks good to me,
>>
>>
>>         Stefan
>>
>>
>> PS: Regarding "eval: (when (fboundp 'rainbow-mode) (rainbow-mode 1))",
>> this is usually considered a misfeature: the file does not need or make
>> special use of rainbow-mode.  Instead, *you* just happen to like using
>> rainbow-mode when editing this file.  So this customization should be in
>> your ~/.emacs rather than in the file itself.
>>
>
>

[-- Attachment #2: Type: text/html, Size: 2672 bytes --]

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: Requesting review before pushing patch to ELPA
  2015-02-05 18:17                   ` Jackson Hamilton
  2015-02-05 18:39                     ` Matthew Carter
  2015-02-05 18:48                     ` Jackson Hamilton
@ 2015-02-05 20:22                     ` Stefan Monnier
  2 siblings, 0 replies; 20+ messages in thread
From: Stefan Monnier @ 2015-02-05 20:22 UTC (permalink / raw)
  To: Jackson Hamilton; +Cc: emacs-devel

> error: failed to push some refs to 'git://git.sv.gnu.org/emacs/elpa'

IIRC this is the anonymous read-only URL.
But, based on other email I got, it looks like you did figure it out.

> error: pack-objects died of signal 13

And yes, this is a confusing error message.


        Stefan



^ permalink raw reply	[flat|nested] 20+ messages in thread

end of thread, other threads:[~2015-02-05 20:22 UTC | newest]

Thread overview: 20+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2015-02-02  7:49 Requesting review before pushing patch to ELPA Jackson Hamilton
2015-02-02 17:58 ` Stefan Monnier
2015-02-03  5:57   ` Jackson Hamilton
2015-02-03 12:13     ` Artur Malabarba
2015-02-04  9:24       ` Jackson Hamilton
2015-02-04 11:15         ` Artur Malabarba
2015-02-04 11:16           ` Artur Malabarba
2015-02-03 17:51     ` Stefan Monnier
2015-02-04  9:32       ` Jackson Hamilton
2015-02-04 15:14         ` Stefan Monnier
2015-02-04 19:46           ` Jackson Hamilton
2015-02-04 19:48             ` Dmitry Gutov
2015-02-04 21:58             ` Stefan Monnier
2015-02-05  8:05               ` Jackson Hamilton
2015-02-05 14:10                 ` Stefan Monnier
2015-02-05 18:17                   ` Jackson Hamilton
2015-02-05 18:39                     ` Matthew Carter
2015-02-05 18:48                     ` Jackson Hamilton
2015-02-05 20:22                     ` Stefan Monnier
2015-02-04 15:13 ` Stefan Monnier

Code repositories for project(s) associated with this external index

	https://git.savannah.gnu.org/cgit/emacs.git
	https://git.savannah.gnu.org/cgit/emacs/org-mode.git

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.