diff --git a/.flake8 b/.flake8 new file mode 100644 index 0000000..982ba2e --- /dev/null +++ b/.flake8 @@ -0,0 +1,66 @@ +# Copyright (c) 2017 Shotgun Software Inc. +# +# CONFIDENTIAL AND PROPRIETARY +# +# This work is provided "AS IS" and subject to the Shotgun Pipeline Toolkit +# Source Code License included in this distribution package. See LICENSE. +# By accessing, using, copying or modifying this work you indicate your +# agreement to the Shotgun Pipeline Toolkit Source Code License. All rights +# not expressly granted therein are reserved by Shotgun Software Inc. + +# Flake 8 PEP and lint configuration - https://gitlab.com/pycqa/flake8 +# +# This defines the official lint and PEP8 rules for this repository +# +# You can run this locally by doing pep install flake8 and then +# >flake8 /path/to/core +# +# This is also used by the hound CI - see the .hound.yml config file. +# +# +[flake8] + +# things we don't want to lint +exclude = + .tox, + .git, + .flake8, + .gitignore, + .travis.yml, + .cache, + .eggs, + *.rst, + *.yml, + *.pyc, + *.pyo, + *.egg-info, + __pycache__, + # Those are our third parties, do not lint them! + python/tank_vendor, + com.sg.basic.ps/*, + cep/node_modules/*, + # Otherwise you'll have a lot of 'xxx' imported but unused + python/tank/__init__.py, + python/tank/*/__init__.py, + # Skip the auto-generated ui file. + python/tank/authentication/ui/login_dialog.py, + python/tank/authentication/ui/resources_rc.py, + python/tank/authentication/sso_saml2/*, + tests/python + +# toolkit exceptions +# +# E501 line too long (112 > 79 characters) +# W291 trailing whitespace +# W293 blank line contains whitespace +# W391 blank line at end of file +# E221 multiple spaces before operator +# E261 two whitespaces before end of line comment. +# E402 module level import not top of file +# N802 Variables should be lower case. (clashes with Qt naming conventions) +# N806 Variables should be lower case. (clashes with Qt naming conventions) +# E999 SyntaxError: invalid syntax (hack for hound CI which runs python 3.x) + +ignore = E501, W291, W293, W391, E221, E261, E402, N802, N806, E999 + + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5388e22 --- /dev/null +++ b/.gitignore @@ -0,0 +1,11 @@ +*.py[co] + +.DS_Store +*.swp +*.lock +profile +Thumbs.db + +*.rnd +*.swp +pkgs/* diff --git a/README.md b/README.md index 0f55f5b..b0d5f7d 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,71 @@ # tk-framework-adobe -A framework for adobe engines + +A framework for Adobe engines + + +# Contributing + +## Setup development environment + +To setup the development environment for this project, you will need to obtain the _**ZXPSignCmd**_ tool provided by Adobe, which can be found [here](https://labs.adobe.com/downloads/extensionbuilder3.html). Once you have logged in using your existing Adobe user account, download the _**CC Extensions Signing Toolkit**_, which will provide you with the necessary executable. + +If you are developing on a ***Mac*** please set all necessary variables in `dev/env.mk`. + +On ***Windows*** please fill all necessary variables in `dev\env.cmd` + +From now on you may test and sign with the following targets: + +--- +***Note*** +Be sure to run all of the following commands from the top-level directory of this project. +--- + + +### To install the CEP extension for testing without signing: +``` +cd dev +make test +``` + +### To sign the CEP extension +``` +cd dev +make sign +``` + + +### To create a certificate for use when signing the CEP extension +``` +cd dev +make create_certificate +``` + +--- +***Note*** +In the case where the configured CERTIFICATE_FILE does not exist, the create_certificate command will be automatically run as part of the _sign_ target. +--- + + +### To remove the latest signed zxp file +``` +cd dev +make clean +``` + +### Notes on editing the env files (env.mk/env.cmd) + +Changes to the env files (env.mk and env.cmd) will typically not be tracked in git. The information contained in these files is specific to a particular development environment, so tracking changes to that data in git is undesirable. + +If you need to make changes to these files, you can use the following commands: + +``` +git update-index --no-skip-worktree dev/env.mk dev/env.cmd +git add dev\env.* +git commit -m "your message" +git update-index --skip-worktree dev/env.mk dev/env.cmd +``` + +Please be aware that these files contain potentially-sensitive information, such as a certificate password. When making changes to these files and pushing them to a git repository, be sure that you've removed any data that might be considered confidential. diff --git a/cep/.debug b/cep/.debug new file mode 100644 index 0000000..c94cd9f --- /dev/null +++ b/cep/.debug @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/cep/CSXS/manifest.xml b/cep/CSXS/manifest.xml new file mode 100644 index 0000000..651a15b --- /dev/null +++ b/cep/CSXS/manifest.xml @@ -0,0 +1,84 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + ./html/manager.html + + --enable-nodejs + + + + + false + + + com.adobe.csxs.events.AppOnline + + com.adobe.csxs.events.ApplicationActivate + + + + + Custom + + + 100 + 100 + + + + + + + + + ./html/panel.html + + --enable-nodejs + + + + true + + + Panel + Shotgun Adobe Panel + + + 350 + 400 + + + + ./images/IconLight.png + ./images/IconLightRollOver.png + ./images/IconDark.png + ./images/IconDarkRollOver.png + + + + + + diff --git a/cep/css/style.css b/cep/css/style.css new file mode 100644 index 0000000..3c1c8d1 --- /dev/null +++ b/cep/css/style.css @@ -0,0 +1,512 @@ +/* +Copyright (c) 2019 Shotgun Software Inc. + +CONFIDENTIAL AND PROPRIETARY + +This work is provided "AS IS" and subject to the Shotgun Pipeline Toolkit Source +Code License included in this distribution package. See LICENSE. By accessing, +using, copying or modifying this work you indicate your agreement to the Shotgun +Pipeline Toolkit Source Code License. All rights not expressly granted therein +are reserved by Shotgun Software Inc. +*/ + +/* fonts... bundled Open Sans fonts which are the SG approved fonts */ +@font-face { + font-family: "Open Sans"; + src: url("../fonts/OpenSans/OpenSans-Regular.ttf"); +} + +@font-face { + font-family: "Open Sans"; + src: url("../fonts/OpenSans/OpenSans-Bold.ttf"); + font-weight: bold; +} +@font-face { + font-family: "Open Sans"; + src: url("../fonts/OpenSans/OpenSans-Italic.ttf"); + font-style: italic; +} + +/* high level defs */ +body { + color: #D7D7D7; + font-family: "Open Sans",Verdana,sans-serif; + font-size: 11px; + margin: 0; + padding: 0; + background-color: #222222; +} + +/* no underlining links */ +a { + text-decoration: none; +} + +a:link { + color: #18A7E3; +} + +a:link.sg_small_link { + color: #18A7E3; + font-size: 10px; +} + +/* links typically found in context header. not SG blue */ +a:link.sg_value_link { + color: #D7D7D7; +} + +a:link.sg_value_link:hover { + color: #FFFFFF; +} + +/* white on hover */ +a:hover { + color: #FFFFFF; +} + +/* shift links slightly when clicking */ +a:active { + position: relative; + top: 1px; + left: 1px +} + +/* placeholder for the context thumbnail */ +#context_thumbnail_data { +} + +/* container for the context field info */ +#context_field_data { + padding-left: 8px; + padding-right: 8px; +} + +/* table for field info */ +#context_field_table { + vertical-align: top; +} + +/* the thumbnail itself */ +#context_thumbnail { + max-width: 178px; + max-height: 100px; + float: left; + padding-right: 8px; + padding-bottom: 8px; +} + +/* the first image that shows up when panel is loading */ +#loading_img { + position: fixed; + margin: auto; + left: 0; + right: 0; + top: 0; + bottom: 0; +} + +/* error display */ +div.sg_error { + width: 90%; + height: 100px; + resize: vertical; + overflow-x: scroll; + overflow-y: auto; + font-size: 10px; + background-color: #2C2C2C; + padding: 8px; + display: inline-block; + text-align: left; +} + +/* scrollbar styling */ +::-webkit-resizer { + background-color: transparent; +} + +::-webkit-scrollbar-track { + background-color: #4A4A4A; + width: 8px; + height: 8px; +} + +::-webkit-scrollbar-track:horizontal { + border-top: 1px solid #222222; +} + +::-webkit-scrollbar { + width: 10px; + height: 10px; +} +::-webkit-scrollbar-thumb { + height: 8px; + width: 8px; + border: 2px solid transparent; + background-clip: padding-box; + -webkit-border-radius: 4px; + background-color: #696969; +} +::-webkit-scrollbar-button { + width: 0; + height: 0; + display: none; +} +::-webkit-scrollbar-corner { + background-color: #4A4A4A; +} + + +#sg_panel_contents { +} + +/* the container for the favorite commands */ +#sg_panel_favorites { + background-color: #525152; + text-align: left; + flex-wrap: wrap; + padding-top: 8px; + padding-bottom: 4px; + margin: 0; + border-bottom: 2px solid #373737; +} + +#sg_panel_favorites_header { + color: #D7D7D7; + padding-left: 20px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +#sg_panel_favorites_shelf { + padding-left: 12px; +} + +.sg_panel_command_img { + width: 19px; + height: 19px; +} + +.sg_panel_command_other_img { + width: 19px; + height: 19px; +} + +/* regular command button */ +.sg_command_button { + display: inline-block; + border-radius: 3px; + margin: 2px; + padding: 4px; + border: 1px solid rgba(0, 0, 0, 0); +} + +.sg_command_button img { + -webkit-filter: grayscale(1) brightness(.8); +} + +.sg_command_button:hover img { + -webkit-filter: grayscale(0) brightness(1); +} + +.sg_command_button:hover { + border: 1px solid #666666; + background-color: #454545; +} + +.sg_command_button_icon { + padding: 2px; + vertical-align: middle; +} + +/* container for the commands */ +#sg_panel_commands { + margin: 0; + background-color: #525152; +} + +/* a single command div */ +.sg_panel_command { + border-bottom: 1px solid #373737; + height: 32px; + padding-left: 16px; + padding-right: 18px; + padding-top: 5px; + color: #D7D7D7; +} + +.sg_panel_command_td { + padding-left: 8px; + max-width: 1px; /* trick to elide text */ + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.sg_panel_command img { + -webkit-filter: grayscale(1) brightness(.8); +} + +.sg_panel_command a:link { + color: #D7D7D7; +} + +.sg_panel_command a:link:hover { + color: white; +} + +.sg_panel_command:hover { + background-color: #6B6B6B; + color: white; +} + +.sg_panel_command:hover img { + -webkit-filter: grayscale(0) brightness(1); +} + +.sg_panel_command:hover a:link { + color: white; +} + +/* mouseover tooltip styling */ +#sg_panel_command_help { + max-width: 186px; + display: inline; + position: absolute; + width: auto; + background-color: rgba(45, 45, 45, .95); + color: #D7D7D7; + border-radius: 3px; + font-size: 10px; + line-height: 13px; + padding: 7px; + overflow: auto; +} + +#sg_panel_footer { + position:fixed; + left:0px; + bottom:0px; + width:100%; +} + +/* the blue status popup section */ +#sg_status_info { + text-align: center; + height: 22px; + line-height: 22px; + color: #D7D7D7; + background-color: rgba(24, 167, 227, .75); +} + +/* the main context header section */ +.sg_context_header { + padding-left: 18px; + padding-right: 8px; + padding-top: 8px; + padding-bottom: 8px; + border-bottom: 2px solid #373737; + background-color: #424242; + min-height: 100px; +} + +/* general styling for labels (less emphasis) */ +.sg_label { + color: #777777; +} + +/* general styling for values (more emphasis) */ +.sg_value { + color: #D7D7D7; +} + +/* context field label td styling */ +.sg_label_td { + display: table-cell; + vertical-align: top; + text-align: right; + color: #777777; +} + +/* context field value td styling */ +.sg_value_td { + display: table-cell; + text-align: left; + vertical-align: top; + color: #D7D7D7; + overflow: hidden; + text-overflow: ellipsis; + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 2; /* number of lines to show */ +} + +.sg_container { + padding: 12px; +} + +.sg_error_message { + padding: 8px; + color: rgba(226, 74, 74, .75); + border-left: 1px solid rgba(226, 74, 74, .75); +} + +#sg_status_error { + width: 100%; + text-align: center; + height: 22px; + line-height: 22px; + color: #D7D7D7; + background-color: rgba(226, 74, 74, .75); +} + +#sg_status_warning { + width: 100%; + text-align: center; + height: 22px; + line-height: 22px; + color: #D7D7D7; + background-color: rgba(226, 159, 74, .75); +} + +#sg_progress { + position: relative; + width: 100%; + height: 8px; + background-color: #222222; + border-bottom: 1px solid #222222; +} + +#sg_progress_bar { + position: absolute; + width: 0%; + height: 8px; + background-color: rgba(24, 167, 227, .75); +} + +/* console styling */ +#sg_panel_console { + position: fixed; + bottom: 0px; + width: 100%; + height: 100%; + background-color: rgba(24, 24, 24, .85); + display: none; +} + +#sg_panel_console_tag { + display: inline; + color: #373737; + border-right: 1px solid #373737; +} + +.sg_panel_console_button { + background-color: #4A4A4A; + border-radius: 3px; + border: 1px solid #343434; + display: inline-block; + color: #D7D7D7; + padding: 4px 8px; + text-decoration: none; + font-family: "Open Sans",Verdana,sans-serif; +} +.sg_panel_console_button:hover { + background-color: #636363; + color: #FFFFFF; +} +.sg_panel_console_button:active { + position:relative; + top:1px; +} + +#sg_panel_console_log { + position: fixed; + top: 0px; + bottom: calc(100% - 44px); + height: calc(100% - 44px); + width: calc(100% - 4px); + padding-left: 4px; + padding-right: 0px; + padding-top: 0px; + padding-bottom: 0px; + text-align: left; + overflow-x: scroll; + overflow-y: scroll; +} + +#sg_panel_console_footer { + position: fixed; + top: calc(100% - 44px); + left: 0px; + width: 100%; + height: 44px; + color: #D7D7D7; + background-color: #4A4A4A; + padding-left: 5px; + padding-right: 0px; + padding-top: 0px; + padding-bottom: 0px; + border-top: 1px solid #222222; + overflow-y:hidden; + overflow-x:scroll; +} + + +#sg_log_message { + color: #D7D7D7; + display: inline; + padding-left: 3px; +} + +#sg_log_message_debug { + color: #18A7E3; + display: inline; + padding-left: 3px; +} + +#sg_log_message_warn { + color: rgb(226, 159, 74); + display: inline; + padding-left: 3px; +} + +#sg_log_message_error { + color: rgb(226, 74, 74); + display: inline; + padding-left: 3px; +} + +#sg_unknown_context_table { + width: 100%; + padding: 10px; + vertical-align: top; +} + +#sg_unknown_context_thumbnail { + display: table-cell; + vertical-align: top; + text-align: center; +} + +#sg_unknown_context_title { + text-align: left; + vertical-align: top; + color: #D7D7D7; + padding-left: 8px; + padding-top: 0; + width: 100%; + flex: 1; + display: inline-block; +} + +#sg_unknown_context_details{ + text-align: left; + vertical-align: top; + color: #777777; + padding-left: 8px; + padding-top: 0px; + width: 100%; + flex: 1; + display: inline-block; +} diff --git a/cep/fonts/OpenSans/OpenSans-Bold.ttf b/cep/fonts/OpenSans/OpenSans-Bold.ttf new file mode 100644 index 0000000..fd79d43 Binary files /dev/null and b/cep/fonts/OpenSans/OpenSans-Bold.ttf differ diff --git a/cep/fonts/OpenSans/OpenSans-Italic.ttf b/cep/fonts/OpenSans/OpenSans-Italic.ttf new file mode 100644 index 0000000..c90da48 Binary files /dev/null and b/cep/fonts/OpenSans/OpenSans-Italic.ttf differ diff --git a/cep/fonts/OpenSans/OpenSans-Regular.ttf b/cep/fonts/OpenSans/OpenSans-Regular.ttf new file mode 100644 index 0000000..db43334 Binary files /dev/null and b/cep/fonts/OpenSans/OpenSans-Regular.ttf differ diff --git a/cep/fonts/OpenSans/OpenSans-Semibold.ttf b/cep/fonts/OpenSans/OpenSans-Semibold.ttf new file mode 100644 index 0000000..1a7679e Binary files /dev/null and b/cep/fonts/OpenSans/OpenSans-Semibold.ttf differ diff --git a/cep/html/manager.html b/cep/html/manager.html new file mode 100644 index 0000000..e266635 --- /dev/null +++ b/cep/html/manager.html @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/cep/html/panel.html b/cep/html/panel.html new file mode 100644 index 0000000..b38d638 --- /dev/null +++ b/cep/html/panel.html @@ -0,0 +1,96 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+ + + + + + + + +
+ + +
+
+ + +
+ + + + diff --git a/cep/icon_256.png b/cep/icon_256.png new file mode 100644 index 0000000..ff2d2a3 Binary files /dev/null and b/cep/icon_256.png differ diff --git a/cep/images/IconDark.png b/cep/images/IconDark.png new file mode 100644 index 0000000..f497308 Binary files /dev/null and b/cep/images/IconDark.png differ diff --git a/cep/images/IconDark@2X.png b/cep/images/IconDark@2X.png new file mode 100644 index 0000000..847c281 Binary files /dev/null and b/cep/images/IconDark@2X.png differ diff --git a/cep/images/IconDarkRollOver.png b/cep/images/IconDarkRollOver.png new file mode 100644 index 0000000..9af8c6a Binary files /dev/null and b/cep/images/IconDarkRollOver.png differ diff --git a/cep/images/IconDarkRollOver@2X.png b/cep/images/IconDarkRollOver@2X.png new file mode 100644 index 0000000..b19463e Binary files /dev/null and b/cep/images/IconDarkRollOver@2X.png differ diff --git a/cep/images/IconLight.png b/cep/images/IconLight.png new file mode 100644 index 0000000..f88374e Binary files /dev/null and b/cep/images/IconLight.png differ diff --git a/cep/images/IconLight@2X.png b/cep/images/IconLight@2X.png new file mode 100644 index 0000000..7efefaf Binary files /dev/null and b/cep/images/IconLight@2X.png differ diff --git a/cep/images/IconLightRollOver.png b/cep/images/IconLightRollOver.png new file mode 100644 index 0000000..90884f7 Binary files /dev/null and b/cep/images/IconLightRollOver.png differ diff --git a/cep/images/IconLightRollOver@2X.png b/cep/images/IconLightRollOver@2X.png new file mode 100644 index 0000000..82c7dc9 Binary files /dev/null and b/cep/images/IconLightRollOver@2X.png differ diff --git a/cep/images/PySideLogo1.png b/cep/images/PySideLogo1.png new file mode 100644 index 0000000..ec381a1 Binary files /dev/null and b/cep/images/PySideLogo1.png differ diff --git a/cep/images/default_Asset_thumb_dark.png b/cep/images/default_Asset_thumb_dark.png new file mode 100644 index 0000000..b9a5337 Binary files /dev/null and b/cep/images/default_Asset_thumb_dark.png differ diff --git a/cep/images/default_Entity_thumb_dark.png b/cep/images/default_Entity_thumb_dark.png new file mode 100644 index 0000000..2b08c92 Binary files /dev/null and b/cep/images/default_Entity_thumb_dark.png differ diff --git a/cep/images/default_Project_thumb_dark.png b/cep/images/default_Project_thumb_dark.png new file mode 100644 index 0000000..a7acdf5 Binary files /dev/null and b/cep/images/default_Project_thumb_dark.png differ diff --git a/cep/images/default_Shot_thumb_dark.png b/cep/images/default_Shot_thumb_dark.png new file mode 100644 index 0000000..8544356 Binary files /dev/null and b/cep/images/default_Shot_thumb_dark.png differ diff --git a/cep/images/default_Site_thumb_dark.png b/cep/images/default_Site_thumb_dark.png new file mode 100644 index 0000000..82c71f7 Binary files /dev/null and b/cep/images/default_Site_thumb_dark.png differ diff --git a/cep/images/default_Task_thumb_dark.png b/cep/images/default_Task_thumb_dark.png new file mode 100644 index 0000000..03c89d4 Binary files /dev/null and b/cep/images/default_Task_thumb_dark.png differ diff --git a/cep/images/external_link.png b/cep/images/external_link.png new file mode 100644 index 0000000..55a1f82 Binary files /dev/null and b/cep/images/external_link.png differ diff --git a/cep/images/sg_logo.png b/cep/images/sg_logo.png new file mode 100644 index 0000000..2a4fefb Binary files /dev/null and b/cep/images/sg_logo.png differ diff --git a/cep/images/sg_logo_with_text.png b/cep/images/sg_logo_with_text.png new file mode 100644 index 0000000..d2e0d18 Binary files /dev/null and b/cep/images/sg_logo_with_text.png differ diff --git a/cep/info.yml b/cep/info.yml new file mode 100644 index 0000000..ea02026 --- /dev/null +++ b/cep/info.yml @@ -0,0 +1,43 @@ +# Copyright (c) 2019 Shotgun Software Inc. +# +# CONFIDENTIAL AND PROPRIETARY +# +# This work is provided "AS IS" and subject to the Shotgun Pipeline Toolkit +# Source Code License included in this distribution package. See LICENSE. +# By accessing, using, copying or modifying this work you indicate your +# agreement to the Shotgun Pipeline Toolkit Source Code License. All rights +# not expressly granted therein are reserved by Shotgun Software Inc. + + +base_configuration: + # The default configuration that the plugin should use. + # For documentation and details, see + # http://developer.shotgunsoftware.com/tk-core/bootstrap.html#sgtk.bootstrap.ToolkitManager.base_configuration + # + # This is expressed in the form of a Toolkit Descriptor. For more + # information about Toolkit descriptors, see + # http://developer.shotgunsoftware.com/tk-core/descriptor.html + # + # If your descriptor supports a version token and you want it to always use + # the latest version available, simply omit the version token. + + # RELEASE : uncomment these. comment out the dev descriptor args below + type: app_store + name: tk-config-basic + + # DEV: uncomment these. comment out the app store descriptor args above + #type: app_store + #name: tk-config-basic + #version: v0.5.2 + +# The Plugin Id helps uniquely identify this plugin and can be used to override +# and customize it. For more information, see: +# http://developer.shotgunsoftware.com/tk-core/bootstrap.html#sgtk.bootstrap.ToolkitManager.plugin_id +# +# When the plugin is built, this file will be converted into a manifest.py file +# and located in a python module named based on the plugin id in order to ensure +# uniqueness. If your plugin_id is set to 'review.maya', the python module +# will be called sgtk_plugin_review_maya and will be located in a python subfolder +# once the plugin has been built. +plugin_id: "basic.adobe" + diff --git a/cep/js/adobe/CSInterface.js b/cep/js/adobe/CSInterface.js new file mode 100644 index 0000000..017784d --- /dev/null +++ b/cep/js/adobe/CSInterface.js @@ -0,0 +1,1194 @@ +/************************************************************************************************** +* +* ADOBE SYSTEMS INCORPORATED +* Copyright 2013 Adobe Systems Incorporated +* All Rights Reserved. +* +* NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the +* terms of the Adobe license agreement accompanying it. If you have received this file from a +* source other than Adobe, then your use, modification, or distribution of it requires the prior +* written permission of Adobe. +* +**************************************************************************************************/ + +/** CSInterface - v7.0.0 */ + +/** + * Stores constants for the window types supported by the CSXS infrastructure. + */ +function CSXSWindowType() +{ +} + +/** Constant for the CSXS window type Panel. */ +CSXSWindowType._PANEL = "Panel"; + +/** Constant for the CSXS window type Modeless. */ +CSXSWindowType._MODELESS = "Modeless"; + +/** Constant for the CSXS window type ModalDialog. */ +CSXSWindowType._MODAL_DIALOG = "ModalDialog"; + +/** EvalScript error message */ +EvalScript_ErrMessage = "EvalScript error."; + +/** + * @class Version + * Defines a version number with major, minor, micro, and special + * components. The major, minor and micro values are numeric; the special + * value can be any string. + * + * @param major The major version component, a positive integer up to nine digits long. + * @param minor The minor version component, a positive integer up to nine digits long. + * @param micro The micro version component, a positive integer up to nine digits long. + * @param special The special version component, an arbitrary string. + * + * @return A new \c Version object. + */ +function Version(major, minor, micro, special) +{ + this.major = major; + this.minor = minor; + this.micro = micro; + this.special = special; +} + +/** + * The maximum value allowed for a numeric version component. + * This reflects the maximum value allowed in PlugPlug and the manifest schema. + */ +Version.MAX_NUM = 999999999; + +/** + * @class VersionBound + * Defines a boundary for a version range, which associates a \c Version object + * with a flag for whether it is an inclusive or exclusive boundary. + * + * @param version The \c #Version object. + * @param inclusive True if this boundary is inclusive, false if it is exclusive. + * + * @return A new \c VersionBound object. + */ +function VersionBound(version, inclusive) +{ + this.version = version; + this.inclusive = inclusive; +} + +/** + * @class VersionRange + * Defines a range of versions using a lower boundary and optional upper boundary. + * + * @param lowerBound The \c #VersionBound object. + * @param upperBound The \c #VersionBound object, or null for a range with no upper boundary. + * + * @return A new \c VersionRange object. + */ +function VersionRange(lowerBound, upperBound) +{ + this.lowerBound = lowerBound; + this.upperBound = upperBound; +} + +/** + * @class Runtime + * Represents a runtime related to the CEP infrastructure. + * Extensions can declare dependencies on particular + * CEP runtime versions in the extension manifest. + * + * @param name The runtime name. + * @param version A \c #VersionRange object that defines a range of valid versions. + * + * @return A new \c Runtime object. + */ +function Runtime(name, versionRange) +{ + this.name = name; + this.versionRange = versionRange; +} + +/** +* @class Extension +* Encapsulates a CEP-based extension to an Adobe application. +* +* @param id The unique identifier of this extension. +* @param name The localizable display name of this extension. +* @param mainPath The path of the "index.html" file. +* @param basePath The base path of this extension. +* @param windowType The window type of the main window of this extension. + Valid values are defined by \c #CSXSWindowType. +* @param width The default width in pixels of the main window of this extension. +* @param height The default height in pixels of the main window of this extension. +* @param minWidth The minimum width in pixels of the main window of this extension. +* @param minHeight The minimum height in pixels of the main window of this extension. +* @param maxWidth The maximum width in pixels of the main window of this extension. +* @param maxHeight The maximum height in pixels of the main window of this extension. +* @param defaultExtensionDataXml The extension data contained in the default \c ExtensionDispatchInfo section of the extension manifest. +* @param specialExtensionDataXml The extension data contained in the application-specific \c ExtensionDispatchInfo section of the extension manifest. +* @param requiredRuntimeList An array of \c Runtime objects for runtimes required by this extension. +* @param isAutoVisible True if this extension is visible on loading. +* @param isPluginExtension True if this extension has been deployed in the Plugins folder of the host application. +* +* @return A new \c Extension object. +*/ +function Extension(id, name, mainPath, basePath, windowType, width, height, minWidth, minHeight, maxWidth, maxHeight, + defaultExtensionDataXml, specialExtensionDataXml, requiredRuntimeList, isAutoVisible, isPluginExtension) +{ + this.id = id; + this.name = name; + this.mainPath = mainPath; + this.basePath = basePath; + this.windowType = windowType; + this.width = width; + this.height = height; + this.minWidth = minWidth; + this.minHeight = minHeight; + this.maxWidth = maxWidth; + this.maxHeight = maxHeight; + this.defaultExtensionDataXml = defaultExtensionDataXml; + this.specialExtensionDataXml = specialExtensionDataXml; + this.requiredRuntimeList = requiredRuntimeList; + this.isAutoVisible = isAutoVisible; + this.isPluginExtension = isPluginExtension; +} + +/** + * @class CSEvent + * A standard JavaScript event, the base class for CEP events. + * + * @param type The name of the event type. + * @param scope The scope of event, can be "GLOBAL" or "APPLICATION". + * @param appId The unique identifier of the application that generated the event. + * @param extensionId The unique identifier of the extension that generated the event. + * + * @return A new \c CSEvent object + */ +function CSEvent(type, scope, appId, extensionId) +{ + this.type = type; + this.scope = scope; + this.appId = appId; + this.extensionId = extensionId; +} + +/** Event-specific data. */ +CSEvent.prototype.data = ""; + +/** + * @class SystemPath + * Stores operating-system-specific location constants for use in the + * \c #CSInterface.getSystemPath() method. + * @return A new \c SystemPath object. + */ +function SystemPath() +{ +} + +/** The path to user data. */ +SystemPath.USER_DATA = "userData"; + +/** The path to common files for Adobe applications. */ +SystemPath.COMMON_FILES = "commonFiles"; + +/** The path to the user's default document folder. */ +SystemPath.MY_DOCUMENTS = "myDocuments"; + +/** @deprecated. Use \c #SystemPath.Extension. */ +SystemPath.APPLICATION = "application"; + +/** The path to current extension. */ +SystemPath.EXTENSION = "extension"; + +/** The path to hosting application's executable. */ +SystemPath.HOST_APPLICATION = "hostApplication"; + +/** + * @class ColorType + * Stores color-type constants. + */ +function ColorType() +{ +} + +/** RGB color type. */ +ColorType.RGB = "rgb"; + +/** Gradient color type. */ +ColorType.GRADIENT = "gradient"; + +/** Null color type. */ +ColorType.NONE = "none"; + +/** + * @class RGBColor + * Stores an RGB color with red, green, blue, and alpha values. + * All values are in the range [0.0 to 255.0]. Invalid numeric values are + * converted to numbers within this range. + * + * @param red The red value, in the range [0.0 to 255.0]. + * @param green The green value, in the range [0.0 to 255.0]. + * @param blue The blue value, in the range [0.0 to 255.0]. + * @param alpha The alpha (transparency) value, in the range [0.0 to 255.0]. + * The default, 255.0, means that the color is fully opaque. + * + * @return A new RGBColor object. + */ +function RGBColor(red, green, blue, alpha) +{ + this.red = red; + this.green = green; + this.blue = blue; + this.alpha = alpha; +} + +/** + * @class Direction + * A point value in which the y component is 0 and the x component + * is positive or negative for a right or left direction, + * or the x component is 0 and the y component is positive or negative for + * an up or down direction. + * + * @param x The horizontal component of the point. + * @param y The vertical component of the point. + * + * @return A new \c Direction object. + */ +function Direction(x, y) +{ + this.x = x; + this.y = y; +} + +/** + * @class GradientStop + * Stores gradient stop information. + * + * @param offset The offset of the gradient stop, in the range [0.0 to 1.0]. + * @param rgbColor The color of the gradient at this point, an \c #RGBColor object. + * + * @return GradientStop object. + */ +function GradientStop(offset, rgbColor) +{ + this.offset = offset; + this.rgbColor = rgbColor; +} + +/** + * @class GradientColor + * Stores gradient color information. + * + * @param type The gradient type, must be "linear". + * @param direction A \c #Direction object for the direction of the gradient + (up, down, right, or left). + * @param numStops The number of stops in the gradient. + * @param gradientStopList An array of \c #GradientStop objects. + * + * @return A new \c GradientColor object. + */ +function GradientColor(type, direction, numStops, arrGradientStop) +{ + this.type = type; + this.direction = direction; + this.numStops = numStops; + this.arrGradientStop = arrGradientStop; +} + +/** + * @class UIColor + * Stores color information, including the type, anti-alias level, and specific color + * values in a color object of an appropriate type. + * + * @param type The color type, 1 for "rgb" and 2 for "gradient". + The supplied color object must correspond to this type. + * @param antialiasLevel The anti-alias level constant. + * @param color A \c #RGBColor or \c #GradientColor object containing specific color information. + * + * @return A new \c UIColor object. + */ +function UIColor(type, antialiasLevel, color) +{ + this.type = type; + this.antialiasLevel = antialiasLevel; + this.color = color; +} + +/** + * @class AppSkinInfo + * Stores window-skin properties, such as color and font. All color parameter values are \c #UIColor objects except that systemHighlightColor is \c #RGBColor object. + * + * @param baseFontFamily The base font family of the application. + * @param baseFontSize The base font size of the application. + * @param appBarBackgroundColor The application bar background color. + * @param panelBackgroundColor The background color of the extension panel. + * @param appBarBackgroundColorSRGB The application bar background color, as sRGB. + * @param panelBackgroundColorSRGB The background color of the extension panel, as sRGB. + * @param systemHighlightColor The operating-system highlight color, as sRGB. + * + * @return AppSkinInfo object. + */ +function AppSkinInfo(baseFontFamily, baseFontSize, appBarBackgroundColor, panelBackgroundColor, appBarBackgroundColorSRGB, panelBackgroundColorSRGB, systemHighlightColor) +{ + this.baseFontFamily = baseFontFamily; + this.baseFontSize = baseFontSize; + this.appBarBackgroundColor = appBarBackgroundColor; + this.panelBackgroundColor = panelBackgroundColor; + this.appBarBackgroundColorSRGB = appBarBackgroundColorSRGB; + this.panelBackgroundColorSRGB = panelBackgroundColorSRGB; + this.systemHighlightColor = systemHighlightColor; +} + +/** + * @class HostEnvironment + * Stores information about the environment in which the extension is loaded. + * + * @param appName The application's name. + * @param appVersion The application's version. + * @param appLocale The application's current license locale. + * @param appUILocale The application's current UI locale. + * @param appId The application's unique identifier. + * @param isAppOnline True if the application is currently online. + * @param appSkinInfo An \c #AppSkinInfo object containing the application's default color and font styles. + * + * @return A new \c HostEnvironment object. + */ +function HostEnvironment(appName, appVersion, appLocale, appUILocale, appId, isAppOnline, appSkinInfo) +{ + this.appName = appName; + this.appVersion = appVersion; + this.appLocale = appLocale; + this.appUILocale = appUILocale; + this.appId = appId; + this.isAppOnline = isAppOnline; + this.appSkinInfo = appSkinInfo; +} + +/** + * @class HostCapabilities + * Stores information about the host capabilities. + * + * @param EXTENDED_PANEL_MENU True if the application supports panel menu. + * @param EXTENDED_PANEL_ICONS True if the application supports panel icon. + * @param DELEGATE_APE_ENGINE True if the application supports delegated APE engine. + * @param SUPPORT_HTML_EXTENSIONS True if the application supports HTML extensions. + * @param DISABLE_FLASH_EXTENSIONS True if the application disables FLASH extensions. + * + * @return A new \c HostCapabilities object. + */ +function HostCapabilities(EXTENDED_PANEL_MENU, EXTENDED_PANEL_ICONS, DELEGATE_APE_ENGINE, SUPPORT_HTML_EXTENSIONS, DISABLE_FLASH_EXTENSIONS) +{ + this.EXTENDED_PANEL_MENU = EXTENDED_PANEL_MENU; + this.EXTENDED_PANEL_ICONS = EXTENDED_PANEL_ICONS; + this.DELEGATE_APE_ENGINE = DELEGATE_APE_ENGINE; + this.SUPPORT_HTML_EXTENSIONS = SUPPORT_HTML_EXTENSIONS; + this.DISABLE_FLASH_EXTENSIONS = DISABLE_FLASH_EXTENSIONS; // Since 5.0.0 +} + +/** + * @class ApiVersion + * Stores current api version. + * + * Since 4.2.0 + * + * @param major The major version + * @param minor The minor version. + * @param micro The micro version. + * + * @return ApiVersion object. + */ +function ApiVersion(major, minor, micro) +{ + this.major = major; + this.minor = minor; + this.micro = micro; +} + +/** + * @class MenuItemStatus + * Stores flyout menu item status + * + * Since 5.2.0 + * + * @param menuItemLabel The menu item label. + * @param enabled True if user wants to enable the menu item. + * @param checked True if user wants to check the menu item. + * + * @return MenuItemStatus object. + */ +function MenuItemStatus(menuItemLabel, enabled, checked) +{ + this.menuItemLabel = menuItemLabel; + this.enabled = enabled; + this.checked = checked; +} + +/** + * @class ContextMenuItemStatus + * Stores the status of the context menu item. + * + * Since 5.2.0 + * + * @param menuItemID The menu item id. + * @param enabled True if user wants to enable the menu item. + * @param checked True if user wants to check the menu item. + * + * @return MenuItemStatus object. + */ +function ContextMenuItemStatus(menuItemID, enabled, checked) +{ + this.menuItemID = menuItemID; + this.enabled = enabled; + this.checked = checked; +} +//------------------------------ CSInterface ---------------------------------- + +/** + * @class CSInterface + * This is the entry point to the CEP extensibility infrastructure. + * Instantiate this object and use it to: + * + * + * @return A new \c CSInterface object + */ +function CSInterface() +{ +} + +/** + * User can add this event listener to handle native application theme color changes. + * Callback function gives extensions ability to fine-tune their theme color after the + * global theme color has been changed. + * The callback function should be like below: + * + * @example + * // event is a CSEvent object, but user can ignore it. + * function OnAppThemeColorChanged(event) + * { + * // Should get a latest HostEnvironment object from application. + * var skinInfo = JSON.parse(window.__adobe_cep__.getHostEnvironment()).appSkinInfo; + * // Gets the style information such as color info from the skinInfo, + * // and redraw all UI controls of your extension according to the style info. + * } + */ +CSInterface.THEME_COLOR_CHANGED_EVENT = "com.adobe.csxs.events.ThemeColorChanged"; + +/** The host environment data object. */ +CSInterface.prototype.hostEnvironment = JSON.parse(window.__adobe_cep__.getHostEnvironment()); + +/** Retrieves information about the host environment in which the + * extension is currently running. + * + * @return A \c #HostEnvironment object. + */ +CSInterface.prototype.getHostEnvironment = function() +{ + this.hostEnvironment = JSON.parse(window.__adobe_cep__.getHostEnvironment()); + return this.hostEnvironment; +}; + +/** Closes this extension. */ +CSInterface.prototype.closeExtension = function() +{ + window.__adobe_cep__.closeExtension(); +}; + +/** + * Retrieves a path for which a constant is defined in the system. + * + * @param pathType The path-type constant defined in \c #SystemPath , + * + * @return The platform-specific system path string. + */ +CSInterface.prototype.getSystemPath = function(pathType) +{ + var path = decodeURI(window.__adobe_cep__.getSystemPath(pathType)); + var OSVersion = this.getOSInformation(); + if (OSVersion.indexOf("Windows") >= 0) + { + path = path.replace("file:///", ""); + } + else if (OSVersion.indexOf("Mac") >= 0) + { + path = path.replace("file://", ""); + } + return path; +}; + + +/** + * Evaluates a JavaScript script, which can use the JavaScript DOM + * of the host application. + * + * @param script The JavaScript script. + * @param callback Optional. A callback function that receives the result of execution. + * If execution fails, the callback function receives the error message \c EvalScript_ErrMessage. + */ +CSInterface.prototype.evalScript = function(script, callback) +{ + if(callback === null || callback === undefined) + { + callback = function(result){}; + } + window.__adobe_cep__.evalScript(script, callback); +}; + +/** + * Retrieves the unique identifier of the application. + * in which the extension is currently running. + * + * @return The unique ID string. + */ +CSInterface.prototype.getApplicationID = function() +{ + var appId = this.hostEnvironment.appId; + return appId; +}; + +/** + * Retrieves host capability information for the application + * in which the extension is currently running. + * + * @return A \c #HostCapabilities object. + */ +CSInterface.prototype.getHostCapabilities = function() +{ + var hostCapabilities = JSON.parse(window.__adobe_cep__.getHostCapabilities() ); + return hostCapabilities; +}; + +/** + * Triggers a CEP event programmatically. Yoy can use it to dispatch + * an event of a predefined type, or of a type you have defined. + * + * @param event A \c CSEvent object. + */ +CSInterface.prototype.dispatchEvent = function(event) +{ + if (typeof event.data == "object") + { + event.data = JSON.stringify(event.data); + } + + window.__adobe_cep__.dispatchEvent(event); +}; + +/** + * Registers an interest in a CEP event of a particular type, and + * assigns an event handler. + * The event infrastructure notifies your extension when events of this type occur, + * passing the event object to the registered handler function. + * + * @param type The name of the event type of interest. + * @param listener The JavaScript handler function or method. + * @param obj Optional, the object containing the handler method, if any. + * Default is null. + */ +CSInterface.prototype.addEventListener = function(type, listener, obj) +{ + window.__adobe_cep__.addEventListener(type, listener, obj); +}; + +/** + * Removes a registered event listener. + * + * @param type The name of the event type of interest. + * @param listener The JavaScript handler function or method that was registered. + * @param obj Optional, the object containing the handler method, if any. + * Default is null. + */ +CSInterface.prototype.removeEventListener = function(type, listener, obj) +{ + window.__adobe_cep__.removeEventListener(type, listener, obj); +}; + +/** + * Loads and launches another extension, or activates the extension if it is already loaded. + * + * @param extensionId The extension's unique identifier. + * @param startupParams Not currently used, pass "". + * + * @example + * To launch the extension "help" with ID "HLP" from this extension, call: + * requestOpenExtension("HLP", ""); + * + */ +CSInterface.prototype.requestOpenExtension = function(extensionId, params) +{ + window.__adobe_cep__.requestOpenExtension(extensionId, params); +}; + +/** + * Retrieves the list of extensions currently loaded in the current host application. + * The extension list is initialized once, and remains the same during the lifetime + * of the CEP session. + * + * @param extensionIds Optional, an array of unique identifiers for extensions of interest. + * If omitted, retrieves data for all extensions. + * + * @return Zero or more \c #Extension objects. + */ +CSInterface.prototype.getExtensions = function(extensionIds) +{ + var extensionIdsStr = JSON.stringify(extensionIds); + var extensionsStr = window.__adobe_cep__.getExtensions(extensionIdsStr); + + var extensions = JSON.parse(extensionsStr); + return extensions; +}; + +/** + * Retrieves network-related preferences. + * + * @return A JavaScript object containing network preferences. + */ +CSInterface.prototype.getNetworkPreferences = function() +{ + var result = window.__adobe_cep__.getNetworkPreferences(); + var networkPre = JSON.parse(result); + + return networkPre; +}; + +/** + * Initializes the resource bundle for this extension with property values + * for the current application and locale. + * To support multiple locales, you must define a property file for each locale, + * containing keyed display-string values for that locale. + * See localization documentation for Extension Builder and related products. + * + * Keys can be in the + * form key.value="localized string", for use in HTML text elements. + * For example, in this input element, the localized \c key.value string is displayed + * instead of the empty \c value string: + * + * + * + * @return An object containing the resource bundle information. + */ +CSInterface.prototype.initResourceBundle = function() +{ + var resourceBundle = JSON.parse(window.__adobe_cep__.initResourceBundle()); + var resElms = document.querySelectorAll('[data-locale]'); + for (var n = 0; n < resElms.length; n++) + { + var resEl = resElms[n]; + // Get the resource key from the element. + var resKey = resEl.getAttribute('data-locale'); + if (resKey) + { + // Get all the resources that start with the key. + for (var key in resourceBundle) + { + if (key.indexOf(resKey) === 0) + { + var resValue = resourceBundle[key]; + if (key.length == resKey.length) + { + resEl.innerHTML = resValue; + } + else if ('.' == key.charAt(resKey.length)) + { + var attrKey = key.substring(resKey.length + 1); + resEl[attrKey] = resValue; + } + } + } + } + } + return resourceBundle; +}; + +/** + * Writes installation information to a file. + * + * @return The file path. + */ +CSInterface.prototype.dumpInstallationInfo = function() +{ + return window.__adobe_cep__.dumpInstallationInfo(); +}; + +/** + * Retrieves version information for the current Operating System, + * See http://www.useragentstring.com/pages/Chrome/ for Chrome \c navigator.userAgent values. + * + * @return A string containing the OS version, or "unknown Operation System". + * If user customizes the User Agent by setting CEF command parameter "--user-agent", only + * "Mac OS X" or "Windows" will be returned. + */ +CSInterface.prototype.getOSInformation = function() +{ + var userAgent = navigator.userAgent; + + if ((navigator.platform == "Win32") || (navigator.platform == "Windows")) + { + var winVersion = "Windows"; + var winBit = ""; + if (userAgent.indexOf("Windows") > -1) + { + if (userAgent.indexOf("Windows NT 5.0") > -1) + { + winVersion = "Windows 2000"; + } + else if (userAgent.indexOf("Windows NT 5.1") > -1) + { + winVersion = "Windows XP"; + } + else if (userAgent.indexOf("Windows NT 5.2") > -1) + { + winVersion = "Windows Server 2003"; + } + else if (userAgent.indexOf("Windows NT 6.0") > -1) + { + winVersion = "Windows Vista"; + } + else if (userAgent.indexOf("Windows NT 6.1") > -1) + { + winVersion = "Windows 7"; + } + else if (userAgent.indexOf("Windows NT 6.2") > -1) + { + winVersion = "Windows 8"; + } + else if (userAgent.indexOf("Windows NT 6.3") > -1) + { + winVersion = "Windows 8.1"; + } + else if (userAgent.indexOf("Windows NT 10") > -1) + { + winVersion = "Windows 10"; + } + + if (userAgent.indexOf("WOW64") > -1 || userAgent.indexOf("Win64") > -1) + { + winBit = " 64-bit"; + } + else + { + winBit = " 32-bit"; + } + } + + return winVersion + winBit; + } + else if ((navigator.platform == "MacIntel") || (navigator.platform == "Macintosh")) + { + var result = "Mac OS X"; + + if (userAgent.indexOf("Mac OS X") > -1) + { + result = userAgent.substring(userAgent.indexOf("Mac OS X"), userAgent.indexOf(")")); + result = result.replace(/_/g, "."); + } + + return result; + } + + return "Unknown Operation System"; +}; + +/** + * Opens a page in the default system browser. + * + * Since 4.2.0 + * + * @param url The URL of the page/file to open, or the email address. + * Must use HTTP/HTTPS/file/mailto protocol. For example: + * "http://www.adobe.com" + * "https://github.com" + * "file:///C:/log.txt" + * "mailto:test@adobe.com" + * + * @return One of these error codes:\n + * \n + */ +CSInterface.prototype.openURLInDefaultBrowser = function(url) +{ + return cep.util.openURLInDefaultBrowser(url); +}; + +/** + * Retrieves extension ID. + * + * Since 4.2.0 + * + * @return extension ID. + */ +CSInterface.prototype.getExtensionID = function() +{ + return window.__adobe_cep__.getExtensionId(); +}; + +/** + * Retrieves the scale factor of screen. + * On Windows platform, the value of scale factor might be different from operating system's scale factor, + * since host application may use its self-defined scale factor. + * + * Since 4.2.0 + * + * @return One of the following float number. + * \n + */ +CSInterface.prototype.getScaleFactor = function() +{ + return window.__adobe_cep__.getScaleFactor(); +}; + +/** + * Set a handler to detect any changes of scale factor. This only works on Mac. + * + * Since 4.2.0 + * + * @param handler The function to be called when scale factor is changed. + * + */ +CSInterface.prototype.setScaleFactorChangedHandler = function(handler) +{ + window.__adobe_cep__.setScaleFactorChangedHandler(handler); +}; + +/** + * Retrieves current API version. + * + * Since 4.2.0 + * + * @return ApiVersion object. + * + */ +CSInterface.prototype.getCurrentApiVersion = function() +{ + var apiVersion = JSON.parse(window.__adobe_cep__.getCurrentApiVersion()); + return apiVersion; +}; + +/** + * Set panel flyout menu by an XML. + * + * Since 5.2.0 + * + * Register a callback function for "com.adobe.csxs.events.flyoutMenuClicked" to get notified when a + * menu item is clicked. + * The "data" attribute of event is an object which contains "menuId" and "menuName" attributes. + * + * Register callback functions for "com.adobe.csxs.events.flyoutMenuOpened" and "com.adobe.csxs.events.flyoutMenuClosed" + * respectively to get notified when flyout menu is opened or closed. + * + * @param menu A XML string which describes menu structure. + * An example menu XML: + * + * + * + * + * + * + * + * + * + * + * + * + */ +CSInterface.prototype.setPanelFlyoutMenu = function(menu) +{ + if ("string" != typeof menu) + { + return; + } + + window.__adobe_cep__.invokeSync("setPanelFlyoutMenu", menu); +}; + +/** + * Updates a menu item in the extension window's flyout menu, by setting the enabled + * and selection status. + * + * Since 5.2.0 + * + * @param menuItemLabel The menu item label. + * @param enabled True to enable the item, false to disable it (gray it out). + * @param checked True to select the item, false to deselect it. + * + * @return false when the host application does not support this functionality (HostCapabilities.EXTENDED_PANEL_MENU is false). + * Fails silently if menu label is invalid. + * + * @see HostCapabilities.EXTENDED_PANEL_MENU + */ +CSInterface.prototype.updatePanelMenuItem = function(menuItemLabel, enabled, checked) +{ + var ret = false; + if (this.getHostCapabilities().EXTENDED_PANEL_MENU) + { + var itemStatus = new MenuItemStatus(menuItemLabel, enabled, checked); + ret = window.__adobe_cep__.invokeSync("updatePanelMenuItem", JSON.stringify(itemStatus)); + } + return ret; +}; + + +/** + * Set context menu by XML string. + * + * Since 5.2.0 + * + * There are a number of conventions used to communicate what type of menu item to create and how it should be handled. + * - an item without menu ID or menu name is disabled and is not shown. + * - if the item name is "---" (three hyphens) then it is treated as a separator. The menu ID in this case will always be NULL. + * - Checkable attribute takes precedence over Checked attribute. + * - a PNG icon. For optimal display results please supply a 16 x 16px icon as larger dimensions will increase the size of the menu item. + The Chrome extension contextMenus API was taken as a reference. + https://developer.chrome.com/extensions/contextMenus + * - the items with icons and checkable items cannot coexist on the same menu level. The former take precedences over the latter. + * + * @param menu A XML string which describes menu structure. + * @param callback The callback function which is called when a menu item is clicked. The only parameter is the returned ID of clicked menu item. + * + * @description An example menu XML: + * + * + * + * + * + * + * + * + * + * + * + */ +CSInterface.prototype.setContextMenu = function(menu, callback) +{ + if ("string" != typeof menu) + { + return; + } + + window.__adobe_cep__.invokeAsync("setContextMenu", menu, callback); +}; + +/** + * Set context menu by JSON string. + * + * Since 6.0.0 + * + * There are a number of conventions used to communicate what type of menu item to create and how it should be handled. + * - an item without menu ID or menu name is disabled and is not shown. + * - if the item label is "---" (three hyphens) then it is treated as a separator. The menu ID in this case will always be NULL. + * - Checkable attribute takes precedence over Checked attribute. + * - a PNG icon. For optimal display results please supply a 16 x 16px icon as larger dimensions will increase the size of the menu item. + The Chrome extension contextMenus API was taken as a reference. + * - the items with icons and checkable items cannot coexist on the same menu level. The former take precedences over the latter. + https://developer.chrome.com/extensions/contextMenus + * + * @param menu A JSON string which describes menu structure. + * @param callback The callback function which is called when a menu item is clicked. The only parameter is the returned ID of clicked menu item. + * + * @description An example menu JSON: + * + * { + * "menu": [ + * { + * "id": "menuItemId1", + * "label": "testExample1", + * "enabled": true, + * "checkable": true, + * "checked": false, + * "icon": "./image/small_16X16.png" + * }, + * { + * "id": "menuItemId2", + * "label": "testExample2", + * "menu": [ + * { + * "id": "menuItemId2-1", + * "label": "testExample2-1", + * "menu": [ + * { + * "id": "menuItemId2-1-1", + * "label": "testExample2-1-1", + * "enabled": false, + * "checkable": true, + * "checked": true + * } + * ] + * }, + * { + * "id": "menuItemId2-2", + * "label": "testExample2-2", + * "enabled": true, + * "checkable": true, + * "checked": true + * } + * ] + * }, + * { + * "label": "---" + * }, + * { + * "id": "menuItemId3", + * "label": "testExample3", + * "enabled": false, + * "checkable": true, + * "checked": false + * } + * ] + * } + * + */ +CSInterface.prototype.setContextMenuByJSON = function(menu, callback) +{ + if ("string" != typeof menu) + { + return; + } + + window.__adobe_cep__.invokeAsync("setContextMenuByJSON", menu, callback); +}; + +/** + * Updates a context menu item by setting the enabled and selection status. + * + * Since 5.2.0 + * + * @param menuItemID The menu item ID. + * @param enabled True to enable the item, false to disable it (gray it out). + * @param checked True to select the item, false to deselect it. + */ +CSInterface.prototype.updateContextMenuItem = function(menuItemID, enabled, checked) +{ + var itemStatus = new ContextMenuItemStatus(menuItemID, enabled, checked); + ret = window.__adobe_cep__.invokeSync("updateContextMenuItem", JSON.stringify(itemStatus)); +}; + +/** + * Get the visibility status of an extension window. + * + * Since 6.0.0 + * + * @return true if the extension window is visible; false if the extension window is hidden. + */ +CSInterface.prototype.isWindowVisible = function() +{ + return window.__adobe_cep__.invokeSync("isWindowVisible", ""); +}; + +/** + * Resize extension's content to the specified dimensions. + * 1. Works with modal and modeless extensions in all Adobe products. + * 2. Extension's manifest min/max size constraints apply and take precedence. + * 3. For panel extensions + * 3.1 This works in all Adobe products except: + * * Premiere Pro + * * Prelude + * * After Effects + * 3.2 When the panel is in certain states (especially when being docked), + * it will not change to the desired dimensions even when the + * specified size satisfies min/max constraints. + * + * Since 6.0.0 + * + * @param width The new width + * @param height The new height + */ +CSInterface.prototype.resizeContent = function(width, height) +{ + window.__adobe_cep__.resizeContent(width, height); +}; + +/** + * Register the invalid certificate callback for an extension. + * This callback will be triggered when the extension tries to access the web site that contains the invalid certificate on the main frame. + * But if the extension does not call this function and tries to access the web site containing the invalid certificate, a default error page will be shown. + * + * Since 6.1.0 + * + * @param callback the callback function + */ +CSInterface.prototype.registerInvalidCertificateCallback = function(callback) +{ + return window.__adobe_cep__.registerInvalidCertificateCallback(callback); +}; + +/** + * Register an interest in some key events to prevent them from being sent to the host application. + * + * This function works with modeless extensions and panel extensions. + * Generally all the key events will be sent to the host application for these two extensions if the current focused element + * is not text input or dropdown, + * If you want to intercept some key events and want them to be handled in the extension, please call this function + * in advance to prevent them being sent to the host application. + * + * Since 6.1.0 + * + * @param keyEventsInterest A JSON string describing those key events you are interested in. A null object or + an empty string will lead to removing the interest + * + * This JSON string should be an array, each object has following keys: + * + * keyCode: [Required] represents an OS system dependent virtual key code identifying + * the unmodified value of the pressed key. + * ctrlKey: [optional] a Boolean that indicates if the control key was pressed (true) or not (false) when the event occurred. + * altKey: [optional] a Boolean that indicates if the alt key was pressed (true) or not (false) when the event occurred. + * shiftKey: [optional] a Boolean that indicates if the shift key was pressed (true) or not (false) when the event occurred. + * metaKey: [optional] (Mac Only) a Boolean that indicates if the Meta key was pressed (true) or not (false) when the event occurred. + * On Macintosh keyboards, this is the command key. To detect Windows key on Windows, please use keyCode instead. + * An example JSON string: + * + * [ + * { + * "keyCode": 48 + * }, + * { + * "keyCode": 123, + * "ctrlKey": true + * }, + * { + * "keyCode": 123, + * "ctrlKey": true, + * "metaKey": true + * } + * ] + * + */ +CSInterface.prototype.registerKeyEventsInterest = function(keyEventsInterest) +{ + return window.__adobe_cep__.registerKeyEventsInterest(keyEventsInterest); +}; + +/** + * Set the title of the extension window. + * This function works with modal and modeless extensions in all Adobe products, and panel extensions in Photoshop, InDesign, InCopy, Illustrator, Flash Pro and Dreamweaver. + * + * Since 6.1.0 + * + * @param title The window title. + */ +CSInterface.prototype.setWindowTitle = function(title) +{ + window.__adobe_cep__.invokeSync("setWindowTitle", title); +}; + +/** + * Get the title of the extension window. + * This function works with modal and modeless extensions in all Adobe products, and panel extensions in Photoshop, InDesign, InCopy, Illustrator, Flash Pro and Dreamweaver. + * + * Since 6.1.0 + * + * @return The window title. + */ +CSInterface.prototype.getWindowTitle = function() +{ + return window.__adobe_cep__.invokeSync("getWindowTitle", ""); +}; diff --git a/cep/js/shotgun/ECMA/JSON-js/json2.js b/cep/js/shotgun/ECMA/JSON-js/json2.js new file mode 100644 index 0000000..d4720c0 --- /dev/null +++ b/cep/js/shotgun/ECMA/JSON-js/json2.js @@ -0,0 +1,506 @@ +// json2.js +// 2016-05-01 +// Public Domain. +// NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. +// See http://www.JSON.org/js.html +// This code should be minified before deployment. +// See http://javascript.crockford.com/jsmin.html + +// USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO +// NOT CONTROL. + +// This file creates a global JSON object containing two methods: stringify +// and parse. This file is provides the ES5 JSON capability to ES3 systems. +// If a project might run on IE8 or earlier, then this file should be included. +// This file does nothing on ES5 systems. + +// JSON.stringify(value, replacer, space) +// value any JavaScript value, usually an object or array. +// replacer an optional parameter that determines how object +// values are stringified for objects. It can be a +// function or an array of strings. +// space an optional parameter that specifies the indentation +// of nested structures. If it is omitted, the text will +// be packed without extra whitespace. If it is a number, +// it will specify the number of spaces to indent at each +// level. If it is a string (such as "\t" or " "), +// it contains the characters used to indent at each level. +// This method produces a JSON text from a JavaScript value. +// When an object value is found, if the object contains a toJSON +// method, its toJSON method will be called and the result will be +// stringified. A toJSON method does not serialize: it returns the +// value represented by the name/value pair that should be serialized, +// or undefined if nothing should be serialized. The toJSON method +// will be passed the key associated with the value, and this will be +// bound to the value. + +// For example, this would serialize Dates as ISO strings. + +// Date.prototype.toJSON = function (key) { +// function f(n) { +// // Format integers to have at least two digits. +// return (n < 10) +// ? "0" + n +// : n; +// } +// return this.getUTCFullYear() + "-" + +// f(this.getUTCMonth() + 1) + "-" + +// f(this.getUTCDate()) + "T" + +// f(this.getUTCHours()) + ":" + +// f(this.getUTCMinutes()) + ":" + +// f(this.getUTCSeconds()) + "Z"; +// }; + +// You can provide an optional replacer method. It will be passed the +// key and value of each member, with this bound to the containing +// object. The value that is returned from your method will be +// serialized. If your method returns undefined, then the member will +// be excluded from the serialization. + +// If the replacer parameter is an array of strings, then it will be +// used to select the members to be serialized. It filters the results +// such that only members with keys listed in the replacer array are +// stringified. + +// Values that do not have JSON representations, such as undefined or +// functions, will not be serialized. Such values in objects will be +// dropped; in arrays they will be replaced with null. You can use +// a replacer function to replace those with JSON values. + +// JSON.stringify(undefined) returns undefined. + +// The optional space parameter produces a stringification of the +// value that is filled with line breaks and indentation to make it +// easier to read. + +// If the space parameter is a non-empty string, then that string will +// be used for indentation. If the space parameter is a number, then +// the indentation will be that many spaces. + +// Example: + +// text = JSON.stringify(["e", {pluribus: "unum"}]); +// // text is '["e",{"pluribus":"unum"}]' + +// text = JSON.stringify(["e", {pluribus: "unum"}], null, "\t"); +// // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]' + +// text = JSON.stringify([new Date()], function (key, value) { +// return this[key] instanceof Date +// ? "Date(" + this[key] + ")" +// : value; +// }); +// // text is '["Date(---current time---)"]' + +// JSON.parse(text, reviver) +// This method parses a JSON text to produce an object or array. +// It can throw a SyntaxError exception. + +// The optional reviver parameter is a function that can filter and +// transform the results. It receives each of the keys and values, +// and its return value is used instead of the original value. +// If it returns what it received, then the structure is not modified. +// If it returns undefined then the member is deleted. + +// Example: + +// // Parse the text. Values that look like ISO date strings will +// // be converted to Date objects. + +// myData = JSON.parse(text, function (key, value) { +// var a; +// if (typeof value === "string") { +// a = +// /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value); +// if (a) { +// return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4], +// +a[5], +a[6])); +// } +// } +// return value; +// }); + +// myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) { +// var d; +// if (typeof value === "string" && +// value.slice(0, 5) === "Date(" && +// value.slice(-1) === ")") { +// d = new Date(value.slice(5, -1)); +// if (d) { +// return d; +// } +// } +// return value; +// }); + +// This is a reference implementation. You are free to copy, modify, or +// redistribute. + +/*jslint + eval, for, this +*/ + +/*property + JSON, apply, call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours, + getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join, + lastIndex, length, parse, prototype, push, replace, slice, stringify, + test, toJSON, toString, valueOf +*/ + + +// Create a JSON object only if one does not already exist. We create the +// methods in a closure to avoid creating global variables. + +if (typeof JSON !== "object") { + JSON = {}; +} + +(function () { + "use strict"; + + var rx_one = /^[\],:{}\s]*$/; + var rx_two = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g; + var rx_three = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g; + var rx_four = /(?:^|:|,)(?:\s*\[)+/g; + var rx_escapable = /[\\\"\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g; + var rx_dangerous = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g; + + function f(n) { + // Format integers to have at least two digits. + return n < 10 + ? "0" + n + : n; + } + + function this_value() { + return this.valueOf(); + } + + if (typeof Date.prototype.toJSON !== "function") { + + Date.prototype.toJSON = function () { + + return isFinite(this.valueOf()) + ? this.getUTCFullYear() + "-" + + f(this.getUTCMonth() + 1) + "-" + + f(this.getUTCDate()) + "T" + + f(this.getUTCHours()) + ":" + + f(this.getUTCMinutes()) + ":" + + f(this.getUTCSeconds()) + "Z" + : null; + }; + + Boolean.prototype.toJSON = this_value; + Number.prototype.toJSON = this_value; + String.prototype.toJSON = this_value; + } + + var gap; + var indent; + var meta; + var rep; + + + function quote(string) { + +// If the string contains no control characters, no quote characters, and no +// backslash characters, then we can safely slap some quotes around it. +// Otherwise we must also replace the offending characters with safe escape +// sequences. + + rx_escapable.lastIndex = 0; + return rx_escapable.test(string) + ? "\"" + string.replace(rx_escapable, function (a) { + var c = meta[a]; + return typeof c === "string" + ? c + : "\\u" + ("0000" + a.charCodeAt(0).toString(16)).slice(-4); + }) + "\"" + : "\"" + string + "\""; + } + + + function str(key, holder) { + +// Produce a string from holder[key]. + + var i; // The loop counter. + var k; // The member key. + var v; // The member value. + var length; + var mind = gap; + var partial; + var value = holder[key]; + +// If the value has a toJSON method, call it to obtain a replacement value. + + if (value && typeof value === "object" && + typeof value.toJSON === "function") { + value = value.toJSON(key); + } + +// If we were called with a replacer function, then call the replacer to +// obtain a replacement value. + + if (typeof rep === "function") { + value = rep.call(holder, key, value); + } + +// What happens next depends on the value's type. + + switch (typeof value) { + case "string": + return quote(value); + + case "number": + +// JSON numbers must be finite. Encode non-finite numbers as null. + + return isFinite(value) + ? String(value) + : "null"; + + case "boolean": + case "null": + +// If the value is a boolean or null, convert it to a string. Note: +// typeof null does not produce "null". The case is included here in +// the remote chance that this gets fixed someday. + + return String(value); + +// If the type is "object", we might be dealing with an object or an array or +// null. + + case "object": + +// Due to a specification blunder in ECMAScript, typeof null is "object", +// so watch out for that case. + + if (!value) { + return "null"; + } + +// Make an array to hold the partial results of stringifying this object value. + + gap += indent; + partial = []; + +// Is the value an array? + + if (Object.prototype.toString.apply(value) === "[object Array]") { + +// The value is an array. Stringify every element. Use null as a placeholder +// for non-JSON values. + + length = value.length; + for (i = 0; i < length; i += 1) { + partial[i] = str(i, value) || "null"; + } + +// Join all of the elements together, separated with commas, and wrap them in +// brackets. + + v = partial.length === 0 + ? "[]" + : gap + ? "[\n" + gap + partial.join(",\n" + gap) + "\n" + mind + "]" + : "[" + partial.join(",") + "]"; + gap = mind; + return v; + } + +// If the replacer is an array, use it to select the members to be stringified. + + if (rep && typeof rep === "object") { + length = rep.length; + for (i = 0; i < length; i += 1) { + if (typeof rep[i] === "string") { + k = rep[i]; + v = str(k, value); + if (v) { + partial.push(quote(k) + ( + gap + ? ": " + : ":" + ) + v); + } + } + } + } else { + +// Otherwise, iterate through all of the keys in the object. + + for (k in value) { + if (Object.prototype.hasOwnProperty.call(value, k)) { + v = str(k, value); + if (v) { + partial.push(quote(k) + ( + gap + ? ": " + : ":" + ) + v); + } + } + } + } + +// Join all of the member texts together, separated with commas, +// and wrap them in braces. + + v = partial.length === 0 + ? "{}" + : gap + ? "{\n" + gap + partial.join(",\n" + gap) + "\n" + mind + "}" + : "{" + partial.join(",") + "}"; + gap = mind; + return v; + } + } + +// If the JSON object does not yet have a stringify method, give it one. + + if (typeof JSON.stringify !== "function") { + meta = { // table of character substitutions + "\b": "\\b", + "\t": "\\t", + "\n": "\\n", + "\f": "\\f", + "\r": "\\r", + "\"": "\\\"", + "\\": "\\\\" + }; + JSON.stringify = function (value, replacer, space) { + +// The stringify method takes a value and an optional replacer, and an optional +// space parameter, and returns a JSON text. The replacer can be a function +// that can replace values, or an array of strings that will select the keys. +// A default replacer method can be provided. Use of the space parameter can +// produce text that is more easily readable. + + var i; + gap = ""; + indent = ""; + +// If the space parameter is a number, make an indent string containing that +// many spaces. + + if (typeof space === "number") { + for (i = 0; i < space; i += 1) { + indent += " "; + } + +// If the space parameter is a string, it will be used as the indent string. + + } else if (typeof space === "string") { + indent = space; + } + +// If there is a replacer, it must be a function or an array. +// Otherwise, throw an error. + + rep = replacer; + if (replacer && typeof replacer !== "function" && + (typeof replacer !== "object" || + typeof replacer.length !== "number")) { + throw new Error("JSON.stringify"); + } + +// Make a fake root object containing our value under the key of "". +// Return the result of stringifying the value. + + return str("", {"": value}); + }; + } + + +// If the JSON object does not yet have a parse method, give it one. + + if (typeof JSON.parse !== "function") { + JSON.parse = function (text, reviver) { + +// The parse method takes a text and an optional reviver function, and returns +// a JavaScript value if the text is a valid JSON text. + + var j; + + function walk(holder, key) { + +// The walk method is used to recursively walk the resulting structure so +// that modifications can be made. + + var k; + var v; + var value = holder[key]; + if (value && typeof value === "object") { + for (k in value) { + if (Object.prototype.hasOwnProperty.call(value, k)) { + v = walk(value, k); + if (v !== undefined) { + value[k] = v; + } else { + delete value[k]; + } + } + } + } + return reviver.call(holder, key, value); + } + + +// Parsing happens in four stages. In the first stage, we replace certain +// Unicode characters with escape sequences. JavaScript handles many characters +// incorrectly, either silently deleting them, or treating them as line endings. + + text = String(text); + rx_dangerous.lastIndex = 0; + if (rx_dangerous.test(text)) { + text = text.replace(rx_dangerous, function (a) { + return "\\u" + + ("0000" + a.charCodeAt(0).toString(16)).slice(-4); + }); + } + +// In the second stage, we run the text against regular expressions that look +// for non-JSON patterns. We are especially concerned with "()" and "new" +// because they can cause invocation, and "=" because it can cause mutation. +// But just to be safe, we want to reject all unexpected forms. + +// We split the second stage into 4 regexp operations in order to work around +// crippling inefficiencies in IE's and Safari's regexp engines. First we +// replace the JSON backslash pairs with "@" (a non-JSON character). Second, we +// replace all simple value tokens with "]" characters. Third, we delete all +// open brackets that follow a colon or comma or that begin the text. Finally, +// we look to see that the remaining characters are only whitespace or "]" or +// "," or ":" or "{" or "}". If that is so, then the text is safe for eval. + + if ( + rx_one.test( + text + .replace(rx_two, "@") + .replace(rx_three, "]") + .replace(rx_four, "") + ) + ) { + +// In the third stage we use the eval function to compile the text into a +// JavaScript structure. The "{" operator is subject to a syntactic ambiguity +// in JavaScript: it can begin a block or an object literal. We wrap the text +// in parens to eliminate the ambiguity. + + j = eval("(" + text + ")"); + +// In the optional fourth stage, we recursively walk the new structure, passing +// each name/value pair to a reviver function for possible transformation. + + return (typeof reviver === "function") + ? walk({"": j}, "") + : j; + } + +// If the text is not JSON parseable, then a SyntaxError is thrown. + + throw new SyntaxError("JSON.parse"); + }; + } +}()); diff --git a/cep/js/shotgun/ECMA/rpc.js b/cep/js/shotgun/ECMA/rpc.js new file mode 100644 index 0000000..244faa3 --- /dev/null +++ b/cep/js/shotgun/ECMA/rpc.js @@ -0,0 +1,445 @@ + +// Copyright (c) 2019 Shotgun Software Inc. +// +// CONFIDENTIAL AND PROPRIETARY +// +// This work is provided "AS IS" and subject to the Shotgun Pipeline Toolkit +// Source Code License included in this distribution package. See LICENSE. +// By accessing, using, copying or modifying this work you indicate your +// agreement to the Shotgun Pipeline Toolkit Source Code License. All rights +// not expressly granted therein are reserved by Shotgun Software Inc. + +#include "./JSON-js/json2.js" +#strict on + +__OBJECT_REGISTRY = {}; +__WRAPPER_REGISTRY = {}; +__GLOBAL_SCOPE_WRAPPERS = undefined; + +// This adds a "unique_id" method to any object that's defined. +(function() { + if (Object.prototype.unique_id === undefined) { + var id = 0; + Object.prototype.unique_id = function() { + if (this.__uniqueid === undefined) { + this.__uniqueid = ++id; + } + return this.__uniqueid; + }; + } +})(); + +/* +Registers a wrapper object and records its relationship to a concrete object for +future lookups. + +:param wrapper: The wrapper object to register. +:param obj: The concrete object that the wrapper object wraps. +*/ +function register_object(wrapper, obj) { + if (obj instanceof Array) { + for (i in obj) { + register_object(wrapper, obj[i]); + } + } + else if (typeof obj == 'object' || typeof obj == 'function') { + __OBJECT_REGISTRY[wrapper.unique_id()] = obj; + __WRAPPER_REGISTRY[wrapper.unique_id()] = wrapper; + } +} + +/* +Tests whether the given object is a wrapper object. + +:param obj: The object to test. + +:rtype: boolean +*/ +function is_wrapper(obj) { + if (obj instanceof ObjectWrapper) { + return true; + } + else if (obj instanceof EnumeratorWrapper) { + return true; + } + else if (obj instanceof FunctionWrapper) { + return true; + } + else if (obj instanceof MethodDescriptor) { + return true; + } + else if (obj['__uniqueid'] != undefined) { + return true; + } + + return false; +} + +/* +Prepares the contents of a list of arguments for use in the native ExtendScript +runtime. If wrapper objects are found in the params list, they will be replaced +with their concrete-object equivalent. + +:param params: The list of arguments to prepare. +*/ +function prepare_arguments(params) { + prepped = []; + + if (params == undefined) { + return prepped; + } + + for (var i=0; i 0) { + sg_logging.debug("Loaded extensions:"); + extensions.forEach(function(ext) { + sg_logging.debug( + "--------------------------------------\n" + + ` name: ${ext.name}\n` + + ` id: ${ext.id}\n` + + `mainPath: ${ext.mainPath}\n` + + `basePath: ${ext.basePath}\n` + ); + }); + } + + // log the os information + sg_logging.debug("OS information: " + _cs_interface.getOSInformation()); + + // log the cep api version: + const api_version = _cs_interface.getCurrentApiVersion(); + sg_logging.debug("CEP API Version: " + + api_version.major + "." + + api_version.minor + "." + + api_version.micro + ); + + } catch (error) { + + const error_lines = error.stack.split(/\r?\n/); + const stack_err_msg = error_lines[0]; + + const message = "There was an unexpected error during startup of " + + "the Adobe Shotgun integration:

" + stack_err_msg; + + // log the error in the event that the panel has started and the + // user can click the console + sg_logging.error(message); + sg_logging.error(error.stack); + + // emit the critical error for any listeners to display + _emit_python_critical_error({ + message: message, + stack: error.stack + }); + + // There are no guarantees that the panel has started up, and + // therefore no guarantees that the user has easy access to the + // debug console. Go ahead and display an old school alert box here + // to ensure that they get something. This may look like crap. + alert(message + "\n\n" + error.stack); + } + }; + + // Code to run when the manager extension is unloaded + this.on_unload = function() { + + // This callback never seems to run. This could be because this is an + // "invisible" extension, but it seems like even regular panels never + // have their page "unload" callbacks called. Leaving this here for now + // in the event that this becomes called at some point in the future. + this.shutdown(); + }; + + // Ensure all the manager's components are shutdown properly + // + // Also emits an event for listeners to respond to manager shutdown. + this.shutdown = function() { + + // alert listeners that the manager is shutting down + sg_manager.SHUTTING_DOWN.emit(); + + // ensure the python process is shut down + if (typeof this.python_process !== "undefined") { + sg_logging.debug("Terminating python process..."); + try { + this.python_process.kill(); + sg_logging.debug("Python process terminated successfully."); + } catch(error) { + sg_logging.warning( + "Unable to terminate python process: " + error.stack); + } + } + + // shut down socket.io server + sg_socket_io.SocketManager.stop_socket_server(); + }; + + // ---- private methods + + // Tests whether the extension can run with the current application + const _app_is_supported = function() { + + // supported if the panel menu and html extensions are available + const host_capabilities = _cs_interface.getHostCapabilities(); + return host_capabilities.EXTENDED_PANEL_MENU && + host_capabilities.SUPPORT_HTML_EXTENSIONS; + }; + + const _active_document_check = function(event) { + _cs_interface.evalScript( + // NOTE: Hopefully this is the same across all Adobe CC + // products. If it isn't, then we'll likely want to make + // a manager method that abstracts it away and returns + // the active document after checking which DCC we're in. + // + // UPDATE: It's not the same, at least not for AE. In that + // case you look up the current project and get the path + // from that. This is going to need to be abstracted + // somehow when we break this out of being PS only. + "app.activeDocument.fullName.fsName", + function(result) { + // If the above command fails, then it's because the + // active document is an unsaved file. + if ( result == "EvalScript error." ) { + // If we previously had a path stored and we're in + // this undefined state, then we've switched from a + // saved document to one that isn't and we still + // need to alert clients. + if ( __active_document !== undefined ) { + sg_logging.debug("Active document changed to undefined"); + __active_document = undefined; + sg_socket_io.rpc_active_document_changed(""); + } + } + else { + // If it's changed, then alert clients. + if ( __active_document !== result ) { + sg_logging.debug("Active document changed to " + result); + __active_document = result; + sg_socket_io.rpc_active_document_changed(result); + } + } + } + ); + }; + + // Bootstrap the toolkit python process. + // + // Returns a `child_process.ChildProcess` object for the running + // python process with a bootstrapped toolkit core. + const _bootstrap_python = function(framework_folder, port) { + + const child_process = require("child_process"); + const path = require("path"); + + const app_id = _cs_interface.hostEnvironment.appId; + const engine_name = sg_constants.product_info[app_id].tk_engine_name; + + // The path to this extension. We're also replacing any encoded + // $ characters that might be returned with a literal $. On + // Windows we're seeing the CS interface returning system paths + // without having $ decoded. This becomes a problem when there's + // a $ in the path, like when using roaming user profiles on + // a Windows network. + const ext_dir = framework_folder.replace("%24", "$"); + + // path to the python folder within the extension + const plugin_python_path = path.join(ext_dir, "python"); + + // get a copy of the current environment and append to PYTHONPATH. + // we need to append the plugin's python path so that it can locate the + // manifest and other files necessary for the bootstrap. + if (process.env.PYTHONPATH) { + // append the plugin's python path to the existing env var + process.env.PYTHONPATH += path.delimiter + plugin_python_path; + } else { + // no PYTHONPATH set. set it to the plugin python path + process.env.PYTHONPATH = plugin_python_path; + } + + // Set the port in the environment. The engine will use this when + // establishing a socket client connection. + process.env.SHOTGUN_ADOBE_PORT = port; + + // get the bootstrap python script from the bootstrap python dir + const plugin_bootstrap_py = path.join(plugin_python_path, + "tk_framework_adobe_utils", "plugin_bootstrap.py"); + + sg_logging.debug("Bootstrapping: " + plugin_bootstrap_py); + + // launch a separate process to bootstrap python with toolkit running... + // > cd $ext_dir + // > python /path/to/ext/bootstrap.py + + // use the system installed python + var python_exe_path = "python"; + + if (process.env.SHOTGUN_ADOBE_PYTHON) { + // use the python specified in the environment if it exists + sg_logging.info("Using python executable set in environment variable 'SHOTGUN_ADOBE_PYTHON'."); + python_exe_path = process.env.SHOTGUN_ADOBE_PYTHON; + } + + sg_logging.debug("Spawning child process... "); + sg_logging.debug("Python executable: " + python_exe_path); + sg_logging.debug("Current working directory: " + plugin_python_path); + sg_logging.debug("Executing command: " + + [ + python_exe_path, + plugin_bootstrap_py, + port, + engine_name, + app_id + ].join(" ") + ); + + try { + this.python_process = child_process.spawn( + python_exe_path, + [ + // path to the python bootstrap script + plugin_bootstrap_py, + port, + engine_name, + app_id + ], + { + // start the process from this dir + cwd: plugin_python_path, + // the environment to use for bootstrapping + env: process.env + } + ); + } + catch (error) { + sg_logging.error("Child process failed to spawn: " + error); + throw error; + } + + this.python_process.on("error", function(error) { + sg_logging.error("Python process error: " + error); + }); + + // log stdout from python process + this.python_process.stdout.on("data", function(data) { + sg_logging.python(data.toString()); + }); + + // log stderr from python process + this.python_process.stderr.on("data", function(data) { + sg_logging.python(data.toString()); + }); + + // handle python process disconnection + this.python_process.on("close", _handle_python_close); + + }.bind(this); + + // Python should never be shut down by anything other than the manager. + // So if we're here, something caused it to exit early. Handle any known + // status codes accordingly. + const _handle_python_close = function(code, signal) { + + const error_codes = sg_constants.python_error_codes; + + if (code == error_codes.EXIT_STATUS_NO_PYSIDE) { + // Special case, PySide does not appear to be installed. + __pyside_unavailable = true; + __python_disconnected = true; + sg_logging.error("Python exited because PySide is unavailable."); + sg_manager.PYSIDE_NOT_AVAILABLE.emit(); + } else { + // Fallback case where we don't know why it shut down. + sg_logging.error("Python exited unexpectedly."); + _emit_python_critical_error({ + message: "The Shotgun integration has unexpectedly shut " + + "down. Specifically, the python process that " + + "handles the communication with Shotgun has " + + "been terminated.", + stack: undefined + }); + } + }; + + // Find an open port and send it to the supplied callback + const _get_open_port = function(port_found_callback) { + + // https://nodejs.org/api/http.html#http_class_http_server + const http = require('http'); + + // keep track of how many times we've tried to find an open port + var num_tries = 0; + + // the number of times to try to find an open port + const max_tries = 25; + + // function to try a port. recurses until a port is found or the max + // try limit is reached. + const _try_port = function() { + + ++num_tries; + + // double checking whether we need to continue here. this should + // prevent this method from being called after a suitable port has + // been identified. + if (typeof this.communication_port !== "undefined") { + // the port is defined. no need to continue + return; + } + + // check the current number of tries. if too many, emit a signal + // indicating that a port could not be found + if (num_tries > max_tries) { + _emit_python_critical_error({ + message: "Unable to set up the communication server that " + + "allows the shotgun integration to work. " + + "Specifically, there was a problem identifying " + + "a port to start up the server.", + stack: undefined + }); + return; + } + + // our method to find an open port seems a bit hacky, and not + // entirely failsafe, but hopefully good enough. if you're reading + // this and know a better way to get an open port, please make + // changes. + + // the logic here is to create an http server, and provide 0 to + // listen(). the OS will provide a random port number. it is *NOT* + // guaranteed to be an open port, so we wait until the listening + // event is fired before presuming it can be used. we close the + // server before proceeding and there's no guarantee that some other + // process won't start using it before our communication server is + // started up. + const server = http.createServer(); + + // if we can listen to the port then we're using it and nobody else + // is. close out and forward the port on for use by the + // communication server + server.on( + "listening", + function() { + // listening, so the port is available + this.communication_port = server.address().port; + server.close(); + }.bind(this) + ); + + // if we get an error, we presume that the port is already in use. + server.on( + "error", + function(error) { + const port = server.address().port; + sg_logging.debug("Could not listen on port: " + port); + // will close after this event + } + ); + + // when the server is closed, check to see if we got a port number. + // if so, call the callback. if not, try again + server.on( + "close", + function() { + if (this.communication_port !== undefined) { + // the port is defined. no need to continue + const port = this.communication_port; + sg_logging.debug("Found available port: " + port); + try { + port_found_callback(port); + } catch(error) { + _emit_python_critical_error({ + message: "Unable to set up the communication " + + "server that allows the shotgun " + + "integration to work.", + stack: error.stack + }); + } + } else { + // still no port. try again + _try_port(); + } + }.bind(this) + ); + + // now that we've setup the event callbacks, tell the server to + // listen to a port assigned by the OS. + server.listen(0); + }.bind(this); + + // fake error message to test startup fail. good for debugging. + //var error = new Error(); + //error.message = "This is a fake fail!!! Thrown manually to force an error!"; + //Error.captureStackTrace(error); + //throw error; + + // initiate the port finding + _try_port(); + }.bind(this); + + // Callback for when an open port is found. + const _on_server_port_found = function(port) { + + sg_socket_io.SocketManager.start_socket_server(port, _cs_interface); + + // Register the socket manager for logging. + sg_logging.rpc = sg_socket_io; + + // bootstrap the python process. + function __tmp_bootstrap_python(result){ + if (!result) + { + console.log("Variable " + sg_constants.framework_adobe_env_var + "is not set. Please edit the bootstrap.py of your engine implementation."); + return false; + } + _bootstrap_python(result, port); + return true; + } + _cs_interface.evalScript("$.getenv('" + sg_constants.framework_adobe_env_var + "')", __tmp_bootstrap_python); + }; + + // Reloads the manager + const _reload = function(event) { + + sg_logging.debug("Reloading the manager..."); + + // shutdown the python process + this.shutdown(); + + // remember this extension id to reload it + const extension_id = _cs_interface.getExtensionID(); + + // close the extension + sg_logging.debug(" Closing the python extension."); + _cs_interface.closeExtension(); + + // request relaunch + sg_logging.debug(" Relaunching the manager..."); + _cs_interface.requestOpenExtension(extension_id); + + }.bind(this); + + // Setup listeners for any events that need to be processed by the manager + const _setup_event_listeners = function() { + + sg_logging.debug("Setting up event listeners..."); + + // ---- Events from the panel + sg_panel.REQUEST_MANAGER_RELOAD.connect(_reload); + + // Handle python process disconnected + sg_panel.REGISTERED_COMMAND_TRIGGERED.connect( + function(event) { + sg_logging.debug("Registered Command Triggered: " + event.data); + sg_socket_io.rpc_command(event.data); + } + ); + + // Handle requests for test running. + sg_panel.RUN_TESTS.connect( + function(event) { + sg_logging.debug("Requesting that tk_photoshopcc run tests..."); + sg_socket_io.rpc_run_tests(); + } + ); + + sg_panel.REQUEST_STATE.connect( + function() { + sg_logging.debug("State requested."); + if (__python_disconnected) { + if (__pyside_unavailable) { + sg_manager.PYSIDE_NOT_AVAILABLE.emit(); + } else { + _emit_python_critical_error(__python_disconnected_error); + } + } else { + sg_logging.debug("Awaiting new state from Python..."); + sg_socket_io.rpc_state_requested(); + } + } + ); + + // Keep an eye on the active document. + // + // NOTE: A useful answer from an Adobe employee in the below: + // https://forums.adobe.com/thread/1380138# + // + sg_logging.debug("Registering documentAfterActivate event..."); + _cs_interface.addEventListener( + 'documentAfterActivate', + _active_document_check + ); + + sg_logging.debug("Event listeners created."); + }; + + // Wrapper to emit the python critical error for listeners + const _emit_python_critical_error = function(error) { + __python_disconnected = true; + __python_disconnected_error = error; + sg_manager.CRITICAL_ERROR.emit(error); + }; + +}; + diff --git a/cep/js/shotgun/manager_events.js b/cep/js/shotgun/manager_events.js new file mode 100644 index 0000000..af7a64c --- /dev/null +++ b/cep/js/shotgun/manager_events.js @@ -0,0 +1,51 @@ +// Copyright (c) 2019 Shotgun Software Inc. +// +// CONFIDENTIAL AND PROPRIETARY +// +// This work is provided "AS IS" and subject to the Shotgun Pipeline Toolkit +// Source Code License included in this distribution package. See LICENSE. +// By accessing, using, copying or modifying this work you indicate your +// agreement to the Shotgun Pipeline Toolkit Source Code License. All rights +// not expressly granted therein are reserved by Shotgun Software Inc. + +"use strict"; + +// namespace +var sg_manager = sg_manager || {}; + +// typically as an async response to a REQUEST_STATE event from the panel +sg_event.create_event(sg_manager, "UPDATE_COMMANDS"); + +// typically as an async response to a REQUEST_STATE event from the panel +sg_event.create_event(sg_manager, "UPDATE_CONTEXT_DISPLAY"); + +// typically as an async response to a REQUEST_STATE event from the panel +sg_event.create_event(sg_manager, "UPDATE_CONTEXT_THUMBNAIL"); + +// provides the toolkit log file path for display in panel +sg_event.create_event(sg_manager, "UPDATE_LOG_FILE_PATH"); + +// sent when the python side cannot determine a context for the current document +sg_event.create_event(sg_manager, "UNKNOWN_CONTEXT"); + +// sent just before a context change from python +sg_event.create_event(sg_manager, "CONTEXT_ABOUT_TO_CHANGE"); + +// emits critical errors whereby the manager is not or can not function +// properly. the emitted event will contain a dictionary of the following form: +// +// { +// message: +// stack: +// } +// +// this event is only emitted when something happens that prevents the manager +// from continuing to function properly. +sg_event.create_event(sg_manager, "CRITICAL_ERROR"); + +// this event emitted when the python process cannot bootstrap due to pyside +// not being installed +sg_event.create_event(sg_manager, "PYSIDE_NOT_AVAILABLE"); + +// emitted when the manager is shutting down. allows listeners to act accordingly +sg_event.create_event(sg_manager, "SHUTTING_DOWN"); diff --git a/cep/js/shotgun/panel.js b/cep/js/shotgun/panel.js new file mode 100644 index 0000000..7ab5654 --- /dev/null +++ b/cep/js/shotgun/panel.js @@ -0,0 +1,1181 @@ +// Copyright (c) 2019 Shotgun Software Inc. +// +// CONFIDENTIAL AND PROPRIETARY +// +// This work is provided "AS IS" and subject to the Shotgun Pipeline Toolkit +// Source Code License included in this distribution package. See LICENSE. +// By accessing, using, copying or modifying this work you indicate your +// agreement to the Shotgun Pipeline Toolkit Source Code License. All rights +// not expressly granted therein are reserved by Shotgun Software Inc. + +"use strict"; + +// namespace. not to be confused with the Shotgun Panel app. +var sg_panel = sg_panel || {}; + +// --------------------------------------------------------------------------- +// The panel + +// A singleton "class" to manage the state of the extension's panel display. +sg_panel.Panel = new function() { + + // ---- private vars + + // adobe interface + const _cs_interface = new CSInterface(); + + // keep track of timeout ids for showing and hiding tooltips. these will be + // used over and over during session. + var _show_tooltip_timeout_id = undefined; + var _hide_tooltip_timeout_id = undefined; + + // we keep track of mouse position to make tooltips easier to manage + var _cur_mouse_pos = { + x: undefined, + y: undefined + }; + + // when context menu items are triggered this will be our lookup to know + // what we need to do + var _context_menu_lookup = {}; + + // tells us whether the thumbnail data has already been retrieved for the + // current context. + var _context_thumbnail_data = undefined; + + var _previous_log_level = "debug"; + + var _log_file_path = undefined; + + // ---- public methods + + this.clear = function() { + // clears any state for the current context. + + _context_thumbnail_data = undefined; + _build_flyout_menu([]); + _clear_messages(); + }; + + // Clears the panel's contents and resets it to the loading state. + this.set_panel_loading_state = function() { + + this.clear(); + + _set_bg_color("#222222"); + + _show_header(false); + _set_contents( + ""); + + _show_info(true); + _set_info( + "Loading Shotgun Integration..." + ); + }; + + // Clears the panel's contents and displays a message that it is disabled + this.set_unknown_context_state = function() { + + this.clear(); + + _set_bg_color("#222222"); + + _show_header(false); + _clear_messages(); + + var app_name = _cs_interface.getHostEnvironment().appName; + const app_display_name = sg_constants.product_info[app_name].display_name; + + _set_contents( + ` + + + + +
+ + + + + + + + + +
+ Integration Disabled +
+ The currently active file can't be associated with a + Shotgun context. Try switching to another file or + restarting ${app_display_name}. +
+
` + ); + + }; + + // Clears the current context and displays it as loading a new one. + this.set_context_loading_state = function() { + + this.clear(); + + _set_bg_color("#4D4D4D"); + + var header_html = `
+
+ +
+
+ Loading context... +
+ +
`; + + _set_header(header_html); + _show_header(true); + + var contents_html = "Loading commands..."; + + _set_contents(contents_html); + _show_contents(true); + _clear_messages(); + }; + + // Need to display a tooltip with the supplied info + this.show_command_help = function(title, help, favorite) { + + // clear any existing tooltip hide timeout so that it doesn't disappear + // prematurely + if (_hide_tooltip_timeout_id !== undefined) { + clearTimeout(_hide_tooltip_timeout_id); + } + + // slight delay to mimic typical tooltip display + _show_tooltip_timeout_id = setTimeout( + function(){_on_show_command_help_timeout(help)}, 1500); + + // update the text in the favorite header with this command's title + if (favorite) { + const fav_header_div = document.getElementById("sg_panel_favorites_header"); + fav_header_div.innerHTML = title; + } + }; + + // Need to hide the tooltip + this.hide_command_help = function() { + + // clear any existing timeouts for showing the tooltip. + if (_show_tooltip_timeout_id !== undefined) { + clearTimeout(_show_tooltip_timeout_id); + } + + // turn off the command help div + _show_command_help(false); + + // reset the favorite header text + const fav_header_div = document.getElementById("sg_panel_favorites_header"); + if (fav_header_div) { + fav_header_div.innerHTML = "Run a command"; + } + }; + + // Open an email in default email client. + this.email_support = function(subject, body) { + + const mailto_url = `mailto:support@shotgunsoftware.com?subject=${subject}&body=${body}`; + + sg_logging.debug("Emailing support: " + mailto_url); + + _clear_info(); + _set_progress_info(100, "Composing SG support email..."); + setTimeout(_clear_info, 2000); + + this.open_external_url(mailto_url); + }; + + // Setup the Shotgun integration within the app. + this.on_load = function() { + + try { + + // ensure the panel is in its loading state. + this.set_panel_loading_state(); + + // build the flyout menu. always do this first so we can have access + // to the debug console no matter what happens during bootstrap. + _build_flyout_menu([]); + + // Listen for the Flyout menu clicks + _cs_interface.addEventListener( + "com.adobe.csxs.events.flyoutMenuClicked", + _on_flyout_menu_clicked + ); + + // setup event listeners first so we can react to various events + _setup_event_listeners(); + + _make_persistent(true); + + // track the mouse + document.onmousemove = _on_mouse_move; + + } catch(error) { + sg_logging.error("Manager startup error: " + error.stack); + alert("Manager startup error: " + error.stack); + } + + // Here we send the "AppOnline" event strictly in the event of a manual + // reload/restart of the SG extension. For initial launch of PS, this + // will be a no-op since the manager will already be running. For the + // reload scenario, this is the jumpstart that the manager requires to + // start up. + const event_type = "com.adobe.csxs.events.AppOnline"; + var event = new CSEvent(event_type, "APPLICATION"); + event.extensionId = _cs_interface.getExtensionID(); + _cs_interface.dispatchEvent(event); + + }.bind(this); + + // Code to run when the extension panel is unloaded + this.on_unload = function() { + sg_logging.debug("Panel unloaded."); + }; + + // Open the supplied url in the default browser + this.open_external_url = function(url) { + sg_logging.debug("Opening external url: " + url); + _cs_interface.openURLInDefaultBrowser(url); + }; + + // Request reload of the manager. + // + // After requesting manager reload, simply shuts down this extension + // since the manager will restart it. + this.reload = function() { + + sg_logging.debug("Closing the panel."); + + // turn off persistence so we can close the panel + _make_persistent(false); + + // close the panel + this.on_unload(); + + // request manager reload and close the panel + sg_panel.REQUEST_MANAGER_RELOAD.emit(); + _cs_interface.closeExtension(); + }; + + // Copy selection to the clipboard + this.selection_to_clipboard = function() { + var selected_text = ""; + if (window.getSelection){ + selected_text = window.getSelection().toString() + } + + if (!selected_text) { + sg_logging.warn("Nothing selected to copy.") + } + + try{ + document.execCommand("copy"); + } catch(e){ + sg_logging.error(`Failed to copy selection to clipboard!\n${e}`); + } + }; + + // Select all the text within the provided div + this.select_text = function(div_id) { + + document.getElementById(div_id).focus(); + + if (document.selection) { + const range = document.body.createTextRange(); + range.moveToElementText(document.getElementById(div_id)); + range.select(); + } else if (window.getSelection) { + const range = document.createRange(); + range.selectNodeContents(document.getElementById(div_id)); + const selection = window.getSelection(); + selection.removeAllRanges(); + selection.addRange(range); + } + }; + + + // Given thumbnail from python, display it in the header + this.set_context_thumbnail = function(context_thumbnail_data) { + + sg_logging.debug("Setting thumbnail."); + + // keep the thumbnail data around in case this runs before the context + // fields display is returned + _context_thumbnail_data = context_thumbnail_data; + + const thumb_path = context_thumbnail_data["thumb_path"]; + const url = context_thumbnail_data["url"]; + + var thumb_html = ` + + `; + + _set_div_html("context_thumbnail_data", thumb_html); + }; + + // Given context display from python, display it in the header. + this.set_context_display = function(context_display) { + + sg_logging.debug("Setting context."); + + var context_thumb = ""; + + // if we already have the thumbnail data, display that instead of the default + if (_context_thumbnail_data !== undefined) { + const thumb_path = _context_thumbnail_data["thumb_path"]; + context_thumb = ``; + } + + var header_html = `
+
+ ${context_thumb} +
+
+ ${context_display} +
+ +
`; + + _set_header(header_html); + _show_header(true); + }; + + // Display the registered commands for the current context + this.set_commands = function(all_commands) { + + sg_logging.debug("Setting commands."); + + // Favorite commands + + const favorites = all_commands["favorites"]; + var favorites_html = ""; + + if (favorites.length > 0) { + + favorites_html = `
+
Run a command
+
`; + + // loop over favorites here + favorites.forEach(function(favorite) { + if (favorite.hasOwnProperty("uid") && + favorite.hasOwnProperty("display_name") && + favorite.hasOwnProperty("icon_path")) { + + const command_id = favorite["uid"]; + const display_name = favorite["display_name"]; + const icon_path = favorite["icon_path"]; + const description = favorite["description"]; + + favorites_html += + ` +
+
+ +
+
+
` + } else { + sg_logging.warn("Favorite command missing required info: " + favorite); + } + }); + + favorites_html += "
"; + } + + // Now process the non-favorite commands + + const commands = all_commands["commands"]; + var commands_html = ""; + + if (commands.length > 0) { + + commands_html = "
"; + + commands.forEach(function(command) { + if (command.hasOwnProperty("uid") && + command.hasOwnProperty("display_name") && + command.hasOwnProperty("icon_path")) { + + const command_id = command["uid"]; + const display_name = command["display_name"]; + const icon_path = command["icon_path"]; + const description = command["description"]; + + commands_html += + ` +
+ + + + + + + + + + +
+ + + ${display_name} +
+
+
`; + } else { + sg_logging.warn("Command missing required info: " + command); + } + }); + + commands_html += "
"; + } + + _set_contents(favorites_html + commands_html); + _show_contents(true); + + // make sure the progress bar and info is hidden + _show_progress(false); + _show_info(false); + + // now build the context menu with the context menu commands + const context_menu_cmds = all_commands["context_menu_cmds"]; + _build_flyout_menu(context_menu_cmds); + + }; + + // Show or hide the console. + this.show_console = function(show) { + + const console_div_id = sg_constants.panel_div_ids["console"]; + const console_log_div_id = sg_constants.panel_div_ids["console_log"]; + + _show_div(console_div_id, show); + + if (show) { + _scroll_to_log_bottom(); + document.body.style.overflow = 'hidden'; + } else { + document.body.style.overflow = 'visible'; + } + }; + + // Emits the signal to launch the supplied command id. + // Also shows a tmp message to confirm user click + this.trigger_command = function(command_id, command_display) { + + // show the progress message temporarily + _set_info("Launching: " + command_display); + setTimeout(_clear_info, 2000); + + // trigger the command + sg_panel.REGISTERED_COMMAND_TRIGGERED.emit(command_id); + }; + + // ---- private methods + + // Builds the flyout menu with the debug/reload options. + const _build_flyout_menu = function(context_menu_cmds) { + + // clear the context menu lookup + _context_menu_lookup = {}; + + // the xml that defines the flyout menu + var flyout_xml = ""; + + context_menu_cmds.forEach(function(command) { + if (command.hasOwnProperty("uid") && + command.hasOwnProperty("display_name") && + command.hasOwnProperty("icon_path")) { + + const command_id = command["uid"]; + const display_name = command["display_name"]; + + _context_menu_lookup[command_id] = display_name; + + flyout_xml += + ''; + } + }); + + flyout_xml += ' \ + '; + + if (process.env.SHOTGUN_ADOBE_NETWORK_DEBUG || + process.env.SHOTGUN_ADOBE_TESTS_ROOT || + process.env.TK_DEBUG) { + + flyout_xml += ''; + + flyout_xml += ''; + } + + if (process.env.SHOTGUN_ADOBE_TESTS_ROOT) { + flyout_xml += ' ' + } + flyout_xml += ''; + + // build the menu + _cs_interface.setPanelFlyoutMenu(flyout_xml); + }; + + // Provides a way to make the panel persistent. + // + // Only valid for Photoshop. + const _make_persistent = function(persistent) { + + const photoshop_ids = ["PHSP", "PHXS"]; + var app_name = ""; + if (photoshop_ids.indexOf(_cs_interface.getApplicationID()) > -1) { + app_name = "Photoshop"; + } else if (_cs_interface.getApplicationID() == "AEFT") { + app_name = "AfterEffects" + } else { + sg_logging.debug("Cannot make the app (un)persistent because the host application doesn't support it."); + return false; + } + + var event_type = persistent ? + 'com.adobe.' + app_name + 'Persistent' : + 'com.adobe.' + app_name + 'UnPersistent'; + + var event = new CSEvent(event_type, "APPLICATION"); + event.extensionId = _cs_interface.getExtensionID(); + _cs_interface.dispatchEvent(event); + return true; + }; + + // Handles flyout menu clicks + const _on_flyout_menu_clicked = function(event) { + + const cmd_id = event.data.menuId; + const cmd_name = event.data.menuName; + + switch (cmd_id) { + + // NOTE: Looks like you can't use `const` in the switch cases. + // The panel won't even load if you do. Perhaps some type of failed + // optimization when doing the menu callback? No obvious errors + // are displayed. Leaving this here as a warning. + + // debug console + case "sg_dev_debug": + sg_logging.debug("Opening debugger in default browser."); + var app_name = _cs_interface.getHostEnvironment().appName; + var debug_url = sg_constants.product_info[app_name].debug_url; + this.open_external_url(debug_url); + break; + + // reload extension + case "sg_dev_reload": + this.reload(); + break; + + // about the extension + case "sg_console": + this.show_console(true); + break; + + // run test suite + case "sg_dev_tests": + sg_logging.debug("Emitting RUN_TESTS panel event."); + sg_panel.RUN_TESTS.emit(); + break; + + default: + + // see if the command id matches one of the context menu ids + if (cmd_id in _context_menu_lookup) { + this.trigger_command(cmd_id, cmd_name); + + // can't determine what this is + } else { + sg_logging.warn( + "Unhandled menu event '" + cmd_name + "' clicked."); + } + } + }.bind(this); + + // Display critical error in the panel + const _on_critical_error = function(message, stack) { + + _set_bg_color("#222222"); + _clear_messages(); + + sg_logging.error("Critical: " + message); + + _show_header(false); + + var contents_html = `
${message}
`; + + contents_html += + `
+ You can try link below to attempt a full restart of the Adobe integration. +

+
+ + Restart Shotgun Integration + +
+
`; + + const subject = encodeURIComponent("Adobe Integration Error"); + const body = _format_email_error_message(message, stack); + + if (typeof stack !== "undefined") { + contents_html += + `
+ If you encounter this problem consistently or have any other + questions, please send the following error and a description + of the steps to reproduce the problem to + + support@shotgunsoftware.com + . +

+
+
${stack}
+
`; + } else { + contents_html += + `
+ If you encounter this problem consistently or have any other + questions, please send the steps to reproduce to + + support@shotgunsoftware.com + .`; + } + + if (typeof _log_file_path !== "undefined") { + contents_html += + `

+ Please attach a copy of the this log file when emailing the + support team:

+ ${_log_file_path} +
+ ` + } + + contents_html = `
${contents_html}
`; + + _set_contents(contents_html); + _set_error( + "Uh oh! Something went wrong." + ); + }; + + // store the mouse position always + const _on_mouse_move = function(event) { + _cur_mouse_pos = { + x: event.clientX, + y: event.clientY + }; + }; + + // Display pyside unavailable error in the panel + const _on_pyside_unavailable = function(event) { + + _set_bg_color("#222222"); + _clear_messages(); + + sg_logging.error("Critical: PySide is unavailable"); + + _show_header(false); + + var python_display = "system python"; + if (process.env.SHOTGUN_ADOBE_PYTHON) { + python_display = "" + process.env.SHOTGUN_ADOBE_PYTHON + ""; + } + + var contents_html = `
+ The Shotgun integration failed to load because PySide + is not installed (Running ${python_display}). +
`; + + contents_html += + `
+ In order for the Shotgun integration to work properly, + PySide must be installed on your system. +

+ For information about PySide and how to install it, + please click the image below: +


+
+ + + +
+
`; + + const subject = encodeURIComponent("Adobe Integration Error"); + const body = encodeURIComponent( + "Greetings Shotgun Support Team!\n\n" + + "We have some questions about the Adobe CC Integration.\n\n" + + "*** Please enter your questions here... ***\n\n" + ); + + var app_name = _cs_interface.getHostEnvironment().appName; + const app_display_name = sg_constants.product_info[app_name].display_name; + + contents_html += + `
+ Once you have PySide installed, restart + ${app_display_name} to load the Shotgun integration. +

+ If you believe the error is incorrect or you have any further + questions, please contact + + support@shotgunsoftware.com + .`; + + contents_html = "
" + contents_html + "
"; + + _set_contents(contents_html); + _set_error( + "Uh Oh! Could not find PySide." + ); + }; + + // Handles incoming log messages + const _on_logged_message = function(event) { + + var level = event.data.level; + var message = event.data.message; + var from_python = event.data.from_python; + + // Some things are sent via log signal because there's no other + // way to get access to them. For example, during toolkit + // bootstrap, we can only gain access to progress via stdio pipe + // maintained between js process and the spawned python process. + // So we intercept messages formatted to relay progress. + if (message.includes("PLUGIN_BOOTSTRAP_PROGRESS")) { + + // It is possible that the message contains multiple + // progress messages packaged together. Identify all of them + // and update the progress bar. + var regex_str = "\\|PLUGIN_BOOTSTRAP_PROGRESS,(\\d+(\\.\\d+)?),([^|]+)\\|"; + + const multi_regex = new RegExp(regex_str, "gm"); + + var matches = message.match(multi_regex); + + if (!matches) { + return; + } + + matches.forEach(function (match) { + const single_regex = new RegExp(regex_str, "m"); + const msg_parts = match.match(single_regex); + // the regex returns the progress value as a float at + // position 1 of the match. position 3 is the message + _set_progress_info(msg_parts[1] * 100, msg_parts[3]); + }); + + return; + } + + var log_source = from_python ? "py" : "js"; + + var messages = undefined; + + if (from_python) { + messages = message.split("\n") + } else { + messages = [message] + } + + messages.forEach(function(msg) { + + // strip newlines + msg = msg.replace(/^\n/, ""); + msg = msg.replace(/\n$/, ""); + + if (msg.startsWith("[DEBUG]: ")) { + msg = msg.replace("[DEBUG]: ", ""); + level = "debug"; + } else if (msg.startsWith("[INFO]: ")) { + msg = msg.replace("[INFO]: ", ""); + level = "info"; + } else if (msg.startsWith("[WARNING]: ")) { + msg = msg.replace("[WARNING]: ", ""); + level = "warn"; + } else if (msg.startsWith("[ERROR]: ")) { + msg = msg.replace("[ERROR]: ", ""); + level = "error"; + } else if (msg.startsWith("[CRITICAL]: ")) { + msg = msg.replace("[ERROR]: ", ""); + level = "error"; + } else if (from_python) { + // account for multi-line log messages from python that may not + // have the expected format + level = _previous_log_level; + } + + if (!msg) { + // continue + return; + } + + // forward message to the panel console + _forward_to_panel_console(level, msg, log_source); + + // forward to the chrome console + console[level](msg); + + _previous_log_level = level; + }); + }; + + // Make the message pretty and add it to the panel's console log + const _forward_to_panel_console = function(level, message, log_source) { + + // figure out which div id to use for style/color + var div_id = "sg_log_message"; + if (level == "debug") { + div_id = "sg_log_message_debug" + } else if (level == "warn") { + div_id = "sg_log_message_warn" + } else if (level == "error") { + div_id = "sg_log_message_error" + } + + // append the
 element to the log div
+        const log = document.getElementById("sg_panel_console_log");
+
+        message.split("\n").forEach(function(msg) {
+
+            if (!msg) {
+                // continue
+                return;
+            }
+
+            // just a little indicator so that we know if the log message came from
+            // (javascript or python) when looking in the panel console.
+            const tag = document.createElement("pre");
+            tag.setAttribute("id", "sg_panel_console_tag");
+            tag.appendChild(document.createTextNode(log_source + " "));
+
+            // create a 
 element and insert the msg
+            const node = document.createElement("pre");
+            node.setAttribute("id", div_id);
+            node.appendChild(document.createTextNode(msg));
+
+            log.appendChild(tag);
+            log.appendChild(node);
+            log.appendChild(document.createElement("br"));
+
+            // scroll to the bottom if an error occurs
+            if (["error", "critical"].indexOf(level) >= 0) {
+                _scroll_to_log_bottom();
+            }
+        });
+    };
+
+    // Scroll to the bottom of the div
+    const _scroll_to_log_bottom = function() {
+
+        const console_log_div_id = sg_constants.panel_div_ids["console_log"];
+        const log = document.getElementById(console_log_div_id);
+        log.scrollTop = log.scrollHeight;
+    };
+
+    // Delay happened, still need to show the command
+    const _on_show_command_help_timeout = function(help) {
+
+        // if no help, display a default message
+        if (!help || help === "null") {
+            help = "Could not find a description for this command. " +
+                   "Please check with the author of the app to see about " +
+                   "making a description available."
+        }
+
+        // mouse pos. always align left to right from mouse position.
+        // if help div will go past right and/or bottom border, adjust accordingly.
+
+        const mouse_x = _cur_mouse_pos.x;
+        const mouse_y = _cur_mouse_pos.y;
+
+        const command_div = document.elementFromPoint(mouse_x, mouse_y);
+
+        const offset = 8;
+        const margin = 8;
+
+        // ---- calculate where to display the help message
+
+        const help_div_id = sg_constants.panel_div_ids["command_help"];
+        const help_div = document.getElementById(help_div_id);
+
+        // reset to the top left to allow it to grow as needed when contest set
+        help_div.style.left = "0px";
+        help_div.style.top = "0px";
+
+        _set_command_help(help);
+
+        const help_div_rect = help_div.getBoundingClientRect();
+
+        const help_width = help_div_rect.width;
+        const help_height = help_div_rect.height;
+
+        const far_right = mouse_x + offset + margin + help_width;
+        const far_bottom = mouse_y + offset + margin + help_height;
+
+        const win_width = window.innerWidth;
+        const win_height = window.innerHeight;
+
+        const beyond_right = far_right - win_width + margin;
+        const beyond_bottom = far_bottom - win_height + margin;
+
+        var adjust_left = 0;
+        var adjust_top = 0;
+
+        if (beyond_right > 0) {
+            adjust_left = -beyond_right;
+        }
+
+        if (beyond_bottom > 0) {
+            adjust_top = -beyond_bottom;
+        }
+
+        const new_left = mouse_x + offset + adjust_left + window.scrollX;
+        const new_top = mouse_y + offset + adjust_top + window.scrollY;
+
+        help_div.style.left = new_left + "px";
+        help_div.style.top = new_top + "px";
+
+        const new_help_div_rect = help_div.getBoundingClientRect();
+
+        if (_point_in_rect(_cur_mouse_pos, new_help_div_rect)) {
+            // the mouse is now inside the help div. need to adjust more
+
+            var additional_offset_y = 0;
+
+            if (beyond_bottom > 0) {
+                // we already adjusted up, keep going. we know we need to get
+                // at least `offset` pixels past the mouse. then it's just the
+                // difference
+                additional_offset_y = -1 * (offset + new_help_div_rect.bottom - mouse_y);
+            }
+
+            help_div.style.top = new_top + additional_offset_y + "px";
+        }
+
+        _show_command_help(true);
+
+        _hide_tooltip_timeout_id = setTimeout(
+            function(){
+                this.hide_command_help()
+            }.bind(this),
+            5000
+        )
+
+    }.bind(this);
+
+    // Returns a boolean indicating if the point is in the rect
+    const _point_in_rect = function(point, rect) {
+
+        return ((point.x >= rect.left) && (point.x <= rect.right) &&
+                (point.y >= rect.top)  && (point.y <= rect.bottom));
+    };
+
+    // Sets up all the event handling callbacks.
+    const _setup_event_listeners = function() {
+
+        // Handle python process disconnected
+        sg_manager.CRITICAL_ERROR.connect(
+            function(event) {
+                const message = event.data.message;
+                const stack = event.data.stack;
+                _on_critical_error(message, stack);
+            }
+        );
+
+        // Handle pyside not being installed
+        sg_manager.PYSIDE_NOT_AVAILABLE.connect(_on_pyside_unavailable);
+
+        // Updates the panel with the current commands from python
+        sg_manager.UPDATE_COMMANDS.connect(
+            function(event) {
+                this.set_commands(event.data);
+            }.bind(this)
+        );
+
+        // Updates the panel with the current context fields from python
+        sg_manager.UPDATE_CONTEXT_DISPLAY.connect(
+            function(event) {
+                this.set_context_display(event.data);
+            }.bind(this)
+        );
+
+        // Updates the panel with the current context thumb path from python
+        sg_manager.UPDATE_CONTEXT_THUMBNAIL.connect(
+            function(event) {
+                this.set_context_thumbnail(event.data);
+            }.bind(this)
+        );
+
+        // Updates the panel with the current log file path
+        sg_manager.UPDATE_LOG_FILE_PATH.connect(
+            function(event) {
+                _log_file_path = event.data;
+            }
+        );
+
+        // Sets the panel into a state where the context is not known
+        sg_manager.UNKNOWN_CONTEXT.connect(
+            function(event) {
+                this.set_unknown_context_state();
+            }.bind(this)
+        );
+
+        // Clears the current context
+        sg_manager.CONTEXT_ABOUT_TO_CHANGE.connect(
+            function(event) {
+                this.set_context_loading_state();
+            }.bind(this)
+        );
+
+        // Handle the manager shutting down.
+        sg_manager.SHUTTING_DOWN.connect(
+            function(event) {
+                _cs_interface.closeExtension();
+            }
+        );
+
+        // Handle log messages from python process
+        sg_logging.LOG_MESSAGE.connect(_on_logged_message);
+
+    }.bind(this);
+
+    // set html for div
+    const _set_div_html = function(div_id, html) {
+        // Updates the inner HTML of the supplied div with the supplied HTML
+        _show_div(div_id, true);
+        document.getElementById(div_id).innerHTML = html;
+    };
+
+    const _set_div_html_by_id = function(div_id) {
+        return function(html) {
+            // Convenience method for updating panel contents with supplied html
+            _set_div_html(sg_constants.panel_div_ids[div_id], html);
+        };
+    };
+
+    // convenience methods for updating the various panel components
+    const _set_contents = _set_div_html_by_id("contents");
+    const _set_header = _set_div_html_by_id("header");
+    const _set_info = _set_div_html_by_id("info");
+    const _set_error = _set_div_html_by_id("error");
+    const _set_warning = _set_div_html_by_id("warning");
+    const _set_command_help = _set_div_html_by_id("command_help");
+
+    // ---- progress bar methods
+
+    // Update the progress section with a % and a message.
+    const _set_progress_info = function(progress, message) {
+
+        if (progress == 100) {
+            // progress is complete. nothing to do
+            _clear_info();
+        } else {
+            _show_progress(true);
+            _show_info(true);
+            var elem = document.getElementById(
+                sg_constants.panel_div_ids["progress_bar"]);
+            elem.style.width = progress + '%';
+            _set_info(message);
+        }
+    };
+
+    // Show or hide a div
+    const _show_div = function(div_id, show_or_hide) {
+        var display = "none";  // hide
+        if (show_or_hide) {
+            display = "block"; // show
+        }
+        var elem = document.getElementById(div_id);
+        elem.style.display = display;
+    };
+
+    const _show_div_by_id = function(div_id) {
+        return function(show_or_hide) {
+            // Convenience method for showing/hiding divs
+            _show_div(sg_constants.panel_div_ids[div_id], show_or_hide);
+        }
+    };
+
+    // convenience methods for showing/hiding status divs
+    const _show_header = _show_div_by_id("header");
+    const _show_contents = _show_div_by_id("contents");
+    const _show_info = _show_div_by_id("info");
+    const _show_error = _show_div_by_id("error");
+    const _show_warning = _show_div_by_id("warning");
+    const _show_progress = _show_div_by_id("progress");
+    const _show_command_help = _show_div_by_id("command_help");
+
+    // sets bg to supplied color
+    const _set_bg_color = function(color) {
+        document.body.style.background = color;
+    };
+
+    // hide all transient divs
+    const _clear_messages = function() {
+        _show_info(false);
+        _show_error(false);
+        _show_warning(false);
+        _show_progress(false);
+        _show_command_help(false);
+    };
+
+    // clears the info related divs
+    const _clear_info = function() {
+        _show_info(false);
+        _show_progress(false);
+    };
+
+    // format an email message to help client get started
+    const _format_email_error_message = function(message, stack) {
+
+        return encodeURIComponent(
+            "Greetings Shotgun Support Team!\n\n" +
+            "We are experiencing some difficulties with the Photoshop CC Integration. " +
+            "The details are included below.\n\n" +
+            "Summary of the issue:\n\n" +
+            "*** Please enter a summary of the issue here... ***\n\n" +
+            "Steps to reproduce:\n\n" +
+            "*** Please enter the steps you took to reach this error here. ***\n\n" +
+            "Error displayed to the user:\n\n" +
+            message + "\n\n" +
+            "Stack trace:\n\n" +
+            stack + "\n\n"
+        );
+    };
+
+};
+
diff --git a/cep/js/shotgun/panel_events.js b/cep/js/shotgun/panel_events.js
new file mode 100644
index 0000000..4254398
--- /dev/null
+++ b/cep/js/shotgun/panel_events.js
@@ -0,0 +1,28 @@
+// Copyright (c) 2019 Shotgun Software Inc.
+//
+// CONFIDENTIAL AND PROPRIETARY
+//
+// This work is provided "AS IS" and subject to the Shotgun Pipeline Toolkit
+// Source Code License included in this distribution package. See LICENSE.
+// By accessing, using, copying or modifying this work you indicate your
+// agreement to the Shotgun Pipeline Toolkit Source Code License. All rights
+// not expressly granted therein are reserved by Shotgun Software Inc.
+
+"use strict";
+
+// namespace. not to be confused with the Shotgun Panel app.
+var sg_panel = sg_panel || {};
+
+// sent when a command is clicked in the panel
+sg_event.create_event(sg_panel, "REGISTERED_COMMAND_TRIGGERED");
+
+// sent from the panel when the user requests to reload the extension
+sg_event.create_event(sg_panel, "REQUEST_MANAGER_RELOAD");
+
+// sent by the panel to request an updated state from the python process
+sg_event.create_event(sg_panel, "REQUEST_STATE");
+
+// sent by the panel to alert Python of the need to run the engines
+// test suite.
+sg_event.create_event(sg_panel, "RUN_TESTS");
+
diff --git a/cep/js/shotgun/socket_io_manager.js b/cep/js/shotgun/socket_io_manager.js
new file mode 100644
index 0000000..4dc16e9
--- /dev/null
+++ b/cep/js/shotgun/socket_io_manager.js
@@ -0,0 +1,458 @@
+// Copyright (c) 2019 Shotgun Software Inc.
+//
+// CONFIDENTIAL AND PROPRIETARY
+//
+// This work is provided "AS IS" and subject to the Shotgun Pipeline Toolkit
+// Source Code License included in this distribution package. See LICENSE.
+// By accessing, using, copying or modifying this work you indicate your
+// agreement to the Shotgun Pipeline Toolkit Source Code License. All rights
+// not expressly granted therein are reserved by Shotgun Software Inc.
+
+"use strict";
+
+// namespace
+var sg_socket_io = sg_socket_io || {};
+
+sg_socket_io.io = undefined;
+
+/*
+Emits the provided payload stringified as JSON via the currently open socket.io
+server.
+
+:param message_type: The type of message to emit.
+:param payload: The payload data to emit as a message.
+*/
+sg_socket_io.emit = function(message_type, payload) {
+    if ( sg_socket_io.io !== undefined ) {
+        sg_socket_io.io.emit(message_type, JSON.stringify(payload));
+    }
+};
+
+/*
+Emits a message that informs any listeners of a change in active document within
+the host application.
+*/
+sg_socket_io.rpc_active_document_changed = function(active_document_path) {
+    sg_logging.debug("Emitting 'active_document_changed' message via socket.io.");
+    var msg = {
+        active_document_path: active_document_path
+    };
+    sg_socket_io.emit("active_document_changed", msg);
+};
+
+/*
+Emits a "logging" message from the currently open socket.io server. The log
+message string and level are combined into a single payload object with "level"
+and "message" properties that is JSON encoded before emission.
+
+:param level: The severity level of the logging message.
+:param message: The logging message.
+*/
+sg_socket_io.rpc_log = function(level, message) {
+    var msg = {
+        level: level,
+        message: message
+    };
+    sg_socket_io.emit("logging", msg);
+};
+
+/*
+Emits a "state_requested" message from the currently open socket.io server.
+*/
+sg_socket_io.rpc_state_requested = function() {
+    sg_logging.debug("Emitting 'state_requested' message via socket.io.");
+    sg_socket_io.emit("state_requested");
+};
+
+/*
+Emits a "command" message from the currently open socket.io server. The given
+uid references an SGTK engine command by the same id, which will be used to look
+up the appropriate callback once the message is handled by a client.
+
+:param uid: The unique id associated with the command to be by the remote client.
+*/
+sg_socket_io.rpc_command = function(uid) {
+    sg_logging.debug("Emitting 'command' message via socket.io.");
+    sg_socket_io.emit("command", uid);
+};
+
+/*
+Emits a "run_tests" message from the currently open socket.io server.
+*/
+sg_socket_io.rpc_run_tests = function() {
+    sg_logging.debug("Emitting 'run_tests' message via socket.io.");
+    sg_socket_io.emit("run_tests", {});
+};
+
+sg_socket_io.SocketManager = new function() {
+
+    var io = undefined;
+
+    /*
+    Emits a debug logging message only if network logging has been
+    turned on via the environment.
+
+    :param msg: The logging message.
+    */
+    var log_network_debug = function(msg) {
+        const legacy_env = process.env.SGTK_PHOTOSHOP_NETWORK_DEBUG;
+        const env_var = process.env.SHOTGUN_ADOBE_NETWORK_DEBUG;
+
+        if ( legacy_env || env_var ) {
+            sg_logging.debug(msg);
+        }
+    };
+
+    /*
+    Replaces Windows-style backslash paths with forward slashes. Also
+    replaces any %24 strings found with $, which is a problem that
+    arises with system paths coming from the CS interface on Windows
+    that contain $ for things like roaming user profiles.
+
+    :param file_path: The file path string to sanitize.
+    */
+    var sanitize_path = function(file_path) {
+        var file_path = file_path.replace(RegExp('\\\\', 'g'), '/');
+        return file_path.replace("%24", "$");
+    };
+
+    /*
+    The callback attached to each JSON-RPC call that's made.
+
+    :param next: The "next" callback that unlocks the socket server event loop.
+    :param result: The result data to be returned back to the RPC caller.
+    */
+    var _eval_callback = function(next, result) {
+        if ( result === "EvalScript error." ) {
+            // We're not going to log here at all. We are notifying any
+            // clients that are listening that the command failed, and
+            // they will have the chance to either recover or raise an
+            // exception on their own.
+            next(true, result);
+        }
+        else {
+            next(false, result);
+        }
+    };
+
+    /*
+    Starts the socket.io server and defines the JSON-RPC interface that is made
+    publicly available to clients.
+
+    :param port: The port number to use when opening the socket.
+    :param csLib: A handle to the standard Adobe CSInterface object.
+    */
+    this.start_socket_server = function (port, csLib) {
+        var path = require('path');
+        var jrpc = require('jrpc');
+        var io = require('socket.io').listen(port);
+        sg_socket_io.io = io;
+
+        sg_logging.info("Listening on port " + JSON.stringify(port));
+
+        // Get the path to the extension.
+        var ext_dir = csLib.getSystemPath(SystemPath.APPLICATION);
+        var js_dir = path.join(ext_dir, "js", "shotgun");
+
+        // Tell ExtendScript to load the rpc.js file that contains our
+        // helper functions.
+        var jsx_rpc_path = sanitize_path(path.join(js_dir, "ECMA", "rpc.js"));
+        var cmd = '$.evalFile("' + jsx_rpc_path + '")';
+        sg_logging.debug("Sourcing rpc.js: " + cmd);
+        csLib.evalScript(cmd);
+
+        sg_logging.info("Establishing jrpc interface.");
+
+        /*
+        The object that defines the JSON-RPC interface exposed by the socket.io
+        server. Each method on this object becomes a callable method over the
+        socket.io connection.
+        */
+        function RPCInterface() {
+
+            /*
+            Maps the global scope of ExtendScript and returns a list of wrapper
+            objects as JSON data. Each wrapper describes the object, its
+            properties, and its methods.
+
+            :param params: The list of parameters associated with the rpc call.
+            :param next: The handle to the "next" callback that triggers the
+                return of data to the caller and causes the next RPC call queued
+                 up to be processed.
+            */
+            this.get_global_scope = function(params, next) {
+                const cmd = "map_global_scope()";
+                log_network_debug(cmd);
+                csLib.evalScript(cmd, _eval_callback.bind(this, next));
+            };
+
+            /*
+            Evalualtes an arbitrary string of Javascript in ExtendScript and
+            returns the resulting data.
+
+            :param params: The list of parameters associated with the rpc call.
+                [extendscript_command]
+            :param next: The handle to the "next" callback that triggers the
+                return of data to the caller and causes the next RPC call queued
+                up to be processed.
+            */
+            this.eval = function(params, next) {
+                log_network_debug(params[0]);
+                csLib.evalScript(params[0], function(result) {
+                    next(false, result);
+                });
+            };
+
+            /*
+            Instantiates an object for the given global-scope class. The given
+            class name must be available in the global scope of ExtendScript at
+            call time.
+
+            :param params: The list of parameters associated with the rpc call.
+                [class_name]
+            :param next: The handle to the "next" callback that triggers the
+                return of data to the caller and causes the next RPC call queued
+                up to be processed.
+            */
+            this.new = function(params, next) {
+                var class_name = JSON.stringify(params.shift());
+                var param_str = JSON.stringify(params);
+                var cmd = "rpc_new(" + class_name + ", " + param_str + ")";
+                log_network_debug(cmd);
+
+                csLib.evalScript(
+                    cmd,
+                    _eval_callback.bind(this, next)
+                );
+            };
+
+            /*
+            Gets the value of the given property on the given object.
+
+            :param params: The list of parameters associated with the rpc call.
+                [object, property_name]
+            :param next: The handle to the "next" callback that triggers the
+                return of data to the caller and causes the next RPC call queued
+                up to be processed.
+            */
+            this.get = function(params, next) {
+                var base = JSON.parse(params.shift());
+                var property = params.shift();
+                var args = [base.__uniqueid, JSON.stringify(property)].join();
+                var cmd = "rpc_get(" + args + ")";
+                log_network_debug(cmd);
+
+                csLib.evalScript(
+                    cmd,
+                    _eval_callback.bind(this, next)
+                );
+            };
+
+            /*
+            Gets the value for the given index number on the given iterable
+                object.
+
+            :param params: The list of parameters associated with the rpc call.
+                [object, index]
+            :param next: The handle to the "next" callback that triggers the
+                return of data to the caller and causes the next RPC call queued
+                up to be processed.
+            */
+            this.get_index = function(params, next) {
+                var base = JSON.parse(params.shift());
+                var index = JSON.stringify(params.shift());
+                var args = [base.__uniqueid, index].join();
+                var cmd = "rpc_get_index(" + args + ")";
+                log_network_debug(cmd);
+
+                csLib.evalScript(
+                    cmd,
+                    _eval_callback.bind(this, next)
+                );
+            };
+
+            /*
+            Compares two objects for equality.
+
+            :param params: A list containing two objects describing what to
+                compare. Each object should contain two properties:
+                value, and is_wrapped. If is_wrapped is true, the comparison
+                will treat the value as an object registry UID and look up
+                the appropriate object for comparison. Otherwise, the value
+                is compared as is.
+            :param next: The handle to the "next" callback that triggers the
+                return of data to the caller and causes the next RPC call queued
+                up to be processed.
+            */
+            this.is_equal = function(params, next) {
+                var left = params.shift();
+                var right = params.shift();
+
+                var left_value = left["value"];
+                var right_value = right["value"];
+
+                if (left["is_wrapped"] === true) {
+                    left_value = "__OBJECT_REGISTRY[" + left_value + "]";
+                }
+                if (right["is_wrapped"] === true) {
+                    right_value = "__OBJECT_REGISTRY[" + right_value + "]";
+                }
+
+                var cmd = left_value + " == " + right_value;
+                log_network_debug(cmd);
+                csLib.evalScript(
+                    cmd,
+                    _eval_callback.bind(this, next)
+                );
+            };
+
+            /*
+            Sets the value of the given property on the given object.
+
+            :param params: The list of parameters associated with the rpc call.
+                [object, property_name, value]
+            :param next: The handle to the "next" callback that triggers the
+                return of data to the caller and causes the
+                next RPC call queued up to be processed.
+            */
+            this.set = function(params, next) {
+                var base = JSON.parse(params.shift());
+                var property = params.shift();
+                var value = params.shift();
+                var args = [
+                    base.__uniqueid,
+                    JSON.stringify(property),
+                    JSON.stringify(value)
+                ].join();
+
+                var cmd = "rpc_set(" + args + ")";
+                log_network_debug(cmd);
+
+                csLib.evalScript(
+                    cmd,
+                    _eval_callback.bind(this, next)
+                );
+            };
+
+            /*
+            Calls the given method on the given object.
+
+            :param params: The list of parameters associated with the rpc call.
+                [method_wrapper, parent_uid, method_arg_1, ...]
+            :param next: The handle to the "next" callback that triggers the
+                return of data to the caller and causes the next RPC call queued
+                up to be processed.
+            */
+            this.call = function(params, next) {
+                var base = JSON.parse(params.shift());
+                // The parent object of the method being called. Since we
+                // need to know what the method is bound to in order to
+                // actually call it (foo.bar(), with "foo" being the parent
+                // object identified by its unique id, and "bar" being the
+                // method itself being called.
+                var parent_uid = params.shift();
+
+                var args = [
+                    base.__uniqueid,
+                    JSON.stringify(params),
+                    parent_uid
+                ].join();
+
+                if ( args.endsWith(",") ) {
+                    args = args + "-1";
+                }
+
+                var cmd = "rpc_call(" + args + ")";
+                log_network_debug(cmd);
+
+                csLib.evalScript(
+                    cmd,
+                    _eval_callback.bind(this, next)
+                );
+            };
+
+        }
+
+        // Stops the socket server.
+        this.stop_socket_server = function() {
+            sg_logging.debug("Shutting down socket server.");
+            io.close();
+        };
+
+        sg_logging.info("Setting up connection handling...");
+
+        const remote = new jrpc();
+        remote.expose(new RPCInterface());
+
+        remote.setTransmitter(function(message, next) {
+            try {
+                io.emit("return", message);
+                return next(false);
+            } catch (e) {
+                return next(true);
+            }
+        });
+
+        // Define the root namespace interface. This will receive all
+        // commands for interacting with ExtendScript.
+        io.on("connection", function(socket) {
+            sg_logging.info("Connection received!");
+
+            socket.on("execute_command", function(message) {
+                remote.receive(message);
+            });
+
+            socket.on("set_commands", function(json_commands) {
+                // The client is setting the commands
+                var commands = JSON.parse(json_commands);
+                sg_logging.debug("Setting commands from client: " + json_commands);
+
+                // TODO: we're emitting a manager event. perhaps we should
+                // have a set of events that come from socket.io? or perhaps
+                // this should call a method on the manager (tried, but doesn't
+                // seem to work!)? but this shouldn't really know about the
+                // manager. anyway, this works, so revisit as time permits.
+                sg_manager.UPDATE_COMMANDS.emit(commands);
+            });
+
+            socket.on("set_context_display", function(json_context_display) {
+                // The client is setting the context display.
+                var context_display = JSON.parse(json_context_display);
+                sg_logging.debug("Setting context display from client.");
+                sg_manager.UPDATE_CONTEXT_DISPLAY.emit(context_display);
+            });
+
+            socket.on("set_context_thumbnail", function(json_context_thumbnail) {
+                // The client is setting the context thumbnail path.
+                var context_thumbnail = JSON.parse(json_context_thumbnail);
+                sg_logging.debug("Setting context thumbnail from client: " + json_context_thumbnail);
+                sg_manager.UPDATE_CONTEXT_THUMBNAIL.emit(context_thumbnail);
+            });
+
+            socket.on("set_log_file_path", function(json_log_file_path) {
+                // The client is setting the current log file path
+                var log_file_path = JSON.parse(json_log_file_path);
+                sg_logging.debug("Setting log file path from client: " + json_log_file_path);
+                sg_manager.UPDATE_LOG_FILE_PATH.emit(log_file_path);
+            });
+
+            socket.on("set_unknown_context", function() {
+                // The context is unknown
+                sg_logging.debug("Sending unknown context signal from the client.");
+                sg_manager.UNKNOWN_CONTEXT.emit();
+            });
+
+            socket.on("context_about_to_change", function() {
+                // The context is about to change
+                sg_logging.debug("Sending context about to change from client.");
+                sg_manager.CONTEXT_ABOUT_TO_CHANGE.emit();
+            });
+
+            socket.on("log_message", function(json_log_data) {
+                // log a message from python
+                var log_data = JSON.parse(json_log_data);
+                sg_logging._log(log_data.level, log_data.msg, false)
+            });
+        });
+    };
+};
diff --git a/cep/node_modules/accepts/HISTORY.md b/cep/node_modules/accepts/HISTORY.md
new file mode 100644
index 0000000..0508481
--- /dev/null
+++ b/cep/node_modules/accepts/HISTORY.md
@@ -0,0 +1,74 @@
+1.1.4 / 2014-12-10
+==================
+
+  * deps: mime-types@~2.0.4
+    - deps: mime-db@~1.3.0
+
+1.1.3 / 2014-11-09
+==================
+
+  * deps: mime-types@~2.0.3
+    - deps: mime-db@~1.2.0
+
+1.1.2 / 2014-10-14
+==================
+
+  * deps: negotiator@0.4.9
+    - Fix error when media type has invalid parameter
+
+1.1.1 / 2014-09-28
+==================
+
+  * deps: mime-types@~2.0.2
+    - deps: mime-db@~1.1.0
+  * deps: negotiator@0.4.8
+    - Fix all negotiations to be case-insensitive
+    - Stable sort preferences of same quality according to client order
+
+1.1.0 / 2014-09-02
+==================
+
+  * update `mime-types`
+
+1.0.7 / 2014-07-04
+==================
+
+  * Fix wrong type returned from `type` when match after unknown extension
+
+1.0.6 / 2014-06-24
+==================
+
+  * deps: negotiator@0.4.7
+
+1.0.5 / 2014-06-20
+==================
+
+ * fix crash when unknown extension given
+
+1.0.4 / 2014-06-19
+==================
+
+  * use `mime-types`
+
+1.0.3 / 2014-06-11
+==================
+
+  * deps: negotiator@0.4.6
+    - Order by specificity when quality is the same
+
+1.0.2 / 2014-05-29
+==================
+
+  * Fix interpretation when header not in request
+  * deps: pin negotiator@0.4.5
+
+1.0.1 / 2014-01-18
+==================
+
+  * Identity encoding isn't always acceptable
+  * deps: negotiator@~0.4.0
+
+1.0.0 / 2013-12-27
+==================
+
+  * Genesis
diff --git a/cep/node_modules/accepts/LICENSE b/cep/node_modules/accepts/LICENSE
new file mode 100644
index 0000000..f23dca8
--- /dev/null
+++ b/cep/node_modules/accepts/LICENSE
@@ -0,0 +1,22 @@
+(The MIT License)
+
+Copyright (c) 2014 Jonathan Ong 
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/cep/node_modules/accepts/README.md b/cep/node_modules/accepts/README.md
new file mode 100644
index 0000000..a9bc28a
--- /dev/null
+++ b/cep/node_modules/accepts/README.md
@@ -0,0 +1,94 @@
+# accepts
+
+[![NPM Version][npm-image]][npm-url]
+[![NPM Downloads][downloads-image]][downloads-url]
+[![Node.js Version][node-version-image]][node-version-url]
+[![Build Status][travis-image]][travis-url]
+[![Test Coverage][coveralls-image]][coveralls-url]
+
+Higher level content negotation based on [negotiator](https://github.com/federomero/negotiator). Extracted from [koa](https://github.com/koajs/koa) for general use.
+
+In addition to negotatior, it allows:
+
+- Allows types as an array or arguments list, ie `(['text/html', 'application/json'])` as well as `('text/html', 'application/json')`.
+- Allows type shorthands such as `json`.
+- Returns `false` when no types match
+- Treats non-existent headers as `*`
+
+## API
+
+### var accept = new Accepts(req)
+
+```js
+var accepts = require('accepts')
+
+http.createServer(function (req, res) {
+  var accept = accepts(req)
+})
+```
+
+### accept\[property\]\(\)
+
+Returns all the explicitly accepted content property as an array in descending priority.
+
+- `accept.types()`
+- `accept.encodings()`
+- `accept.charsets()`
+- `accept.languages()`
+
+They are also aliased in singular form such as `accept.type()`. `accept.languages()` is also aliased as `accept.langs()`, etc.
+
+Note: you should almost never do this in a real app as it defeats the purpose of content negotiation.
+
+Example:
+
+```js
+// in Google Chrome
+var encodings = accept.encodings() // -> ['sdch', 'gzip', 'deflate']
+```
+
+Since you probably don't support `sdch`, you should just supply the encodings you support:
+
+```js
+var encoding = accept.encodings('gzip', 'deflate') // -> 'gzip', probably
+```
+
+### accept\[property\]\(values, ...\)
+
+You can either have `values` be an array or have an argument list of values.
+
+If the client does not accept any `values`, `false` will be returned.
+If the client accepts any `values`, the preferred `value` will be return.
+
+For `accept.types()`, shorthand mime types are allowed.
+
+Example:
+
+```js
+// req.headers.accept = 'application/json'
+
+accept.types('json') // -> 'json'
+accept.types('html', 'json') // -> 'json'
+accept.types('html') // -> false
+
+// req.headers.accept = ''
+// which is equivalent to `*`
+
+accept.types() // -> [], no explicit types
+accept.types('text/html', 'text/json') // -> 'text/html', since it was first
+```
+
+## License
+
+[MIT](LICENSE)
+
+[npm-image]: https://img.shields.io/npm/v/accepts.svg?style=flat
+[npm-url]: https://npmjs.org/package/accepts
+[node-version-image]: https://img.shields.io/node/v/accepts.svg?style=flat
+[node-version-url]: http://nodejs.org/download/
+[travis-image]: https://img.shields.io/travis/jshttp/accepts.svg?style=flat
+[travis-url]: https://travis-ci.org/jshttp/accepts
+[coveralls-image]: https://img.shields.io/coveralls/jshttp/accepts.svg?style=flat
+[coveralls-url]: https://coveralls.io/r/jshttp/accepts
+[downloads-image]: https://img.shields.io/npm/dm/accepts.svg?style=flat
+[downloads-url]: https://npmjs.org/package/accepts
diff --git a/cep/node_modules/accepts/index.js b/cep/node_modules/accepts/index.js
new file mode 100644
index 0000000..805e33a
--- /dev/null
+++ b/cep/node_modules/accepts/index.js
@@ -0,0 +1,160 @@
+var Negotiator = require('negotiator')
+var mime = require('mime-types')
+
+var slice = [].slice
+
+module.exports = Accepts
+
+function Accepts(req) {
+  if (!(this instanceof Accepts))
+    return new Accepts(req)
+
+  this.headers = req.headers
+  this.negotiator = Negotiator(req)
+}
+
+/**
+ * Check if the given `type(s)` is acceptable, returning
+ * the best match when true, otherwise `undefined`, in which
+ * case you should respond with 406 "Not Acceptable".
+ *
+ * The `type` value may be a single mime type string
+ * such as "application/json", the extension name
+ * such as "json" or an array `["json", "html", "text/plain"]`. When a list
+ * or array is given the _best_ match, if any is returned.
+ *
+ * Examples:
+ *
+ *     // Accept: text/html
+ *     this.types('html');
+ *     // => "html"
+ *
+ *     // Accept: text/*, application/json
+ *     this.types('html');
+ *     // => "html"
+ *     this.types('text/html');
+ *     // => "text/html"
+ *     this.types('json', 'text');
+ *     // => "json"
+ *     this.types('application/json');
+ *     // => "application/json"
+ *
+ *     // Accept: text/*, application/json
+ *     this.types('image/png');
+ *     this.types('png');
+ *     // => undefined
+ *
+ *     // Accept: text/*;q=.5, application/json
+ *     this.types(['html', 'json']);
+ *     this.types('html', 'json');
+ *     // => "json"
+ *
+ * @param {String|Array} type(s)...
+ * @return {String|Array|Boolean}
+ * @api public
+ */
+
+Accepts.prototype.type =
+Accepts.prototype.types = function (types) {
+  if (!Array.isArray(types)) types = slice.call(arguments);
+  var n = this.negotiator;
+  if (!types.length) return n.mediaTypes();
+  if (!this.headers.accept) return types[0];
+  var mimes = types.map(extToMime);
+  var accepts = n.mediaTypes(mimes.filter(validMime));
+  var first = accepts[0];
+  if (!first) return false;
+  return types[mimes.indexOf(first)];
+}
+
+/**
+ * Return accepted encodings or best fit based on `encodings`.
+ *
+ * Given `Accept-Encoding: gzip, deflate`
+ * an array sorted by quality is returned:
+ *
+ *     ['gzip', 'deflate']
+ *
+ * @param {String|Array} encoding(s)...
+ * @return {String|Array}
+ * @api public
+ */
+
+Accepts.prototype.encoding =
+Accepts.prototype.encodings = function (encodings) {
+  if (!Array.isArray(encodings)) encodings = slice.call(arguments);
+  var n = this.negotiator;
+  if (!encodings.length) return n.encodings();
+  return n.encodings(encodings)[0] || false;
+}
+
+/**
+ * Return accepted charsets or best fit based on `charsets`.
+ *
+ * Given `Accept-Charset: utf-8, iso-8859-1;q=0.2, utf-7;q=0.5`
+ * an array sorted by quality is returned:
+ *
+ *     ['utf-8', 'utf-7', 'iso-8859-1']
+ *
+ * @param {String|Array} charset(s)...
+ * @return {String|Array}
+ * @api public
+ */
+
+Accepts.prototype.charset =
+Accepts.prototype.charsets = function (charsets) {
+  if (!Array.isArray(charsets)) charsets = [].slice.call(arguments);
+  var n = this.negotiator;
+  if (!charsets.length) return n.charsets();
+  if (!this.headers['accept-charset']) return charsets[0];
+  return n.charsets(charsets)[0] || false;
+}
+
+/**
+ * Return accepted languages or best fit based on `langs`.
+ *
+ * Given `Accept-Language: en;q=0.8, es, pt`
+ * an array sorted by quality is returned:
+ *
+ *     ['es', 'pt', 'en']
+ *
+ * @param {String|Array} lang(s)...
+ * @return {Array|String}
+ * @api public
+ */
+
+Accepts.prototype.lang =
+Accepts.prototype.langs =
+Accepts.prototype.language =
+Accepts.prototype.languages = function (langs) {
+  if (!Array.isArray(langs)) langs = slice.call(arguments);
+  var n = this.negotiator;
+  if (!langs.length) return n.languages();
+  if (!this.headers['accept-language']) return langs[0];
+  return n.languages(langs)[0] || false;
+}
+
+/**
+ * Convert extnames to mime.
+ *
+ * @param {String} type
+ * @return {String}
+ * @api private
+ */
+
+function extToMime(type) {
+  if (~type.indexOf('/')) return type;
+  return mime.lookup(type);
+}
+
+/**
+ * Check if mime is valid.
+ *
+ * @param {String} type
+ * @return {String}
+ * @api private
+ */
+
+function validMime(type) {
+  return typeof type === 'string';
+}
diff --git a/cep/node_modules/accepts/package.json b/cep/node_modules/accepts/package.json
new file mode 100644
index 0000000..2f246c4
--- /dev/null
+++ b/cep/node_modules/accepts/package.json
@@ -0,0 +1,126 @@
+{
+  "_args": [
+    [
+      {
+        "raw": "accepts@1.1.4",
+        "scope": null,
+        "escapedName": "accepts",
+        "name": "accepts",
+        "rawSpec": "1.1.4",
+        "spec": "1.1.4",
+        "type": "version"
+      },
+      "D:\\repositories\\tk-framework-adobe\\extensions\\http_server\\node_modules\\engine.io"
+    ]
+  ],
+  "_from": "accepts@1.1.4",
+  "_id": "accepts@1.1.4",
+  "_inCache": true,
+  "_installable": true,
+  "_location": "/accepts",
+  "_npmUser": {
+    "name": "dougwilson",
+    "email": "doug@somethingdoug.com"
+  },
+  "_npmVersion": "1.4.21",
+  "_phantomChildren": {},
+  "_requested": {
+    "raw": "accepts@1.1.4",
+    "scope": null,
+    "escapedName": "accepts",
+    "name": "accepts",
+    "rawSpec": "1.1.4",
+    "spec": "1.1.4",
+    "type": "version"
+  },
+  "_requiredBy": [
+    "/engine.io"
+  ],
+  "_resolved": "https://registry.npmjs.org/accepts/-/accepts-1.1.4.tgz",
+  "_shasum": "d71c96f7d41d0feda2c38cd14e8a27c04158df4a",
+  "_shrinkwrap": null,
+  "_spec": "accepts@1.1.4",
+  "_where": "D:\\repositories\\tk-framework-adobe\\extensions\\http_server\\node_modules\\engine.io",
+  "author": {
+    "name": "Jonathan Ong",
+    "email": "me@jongleberry.com",
+    "url": "http://jongleberry.com"
+  },
+  "bugs": {
+    "url": "https://github.com/jshttp/accepts/issues"
+  },
+  "dependencies": {
+    "mime-types": "~2.0.4",
+    "negotiator": "0.4.9"
+  },
+  "description": "Higher-level content negotiation",
+  "devDependencies": {
+    "istanbul": "~0.3.4",
+    "mocha": "~2.0.1"
+  },
+  "directories": {},
+  "dist": {
+    "shasum": "d71c96f7d41d0feda2c38cd14e8a27c04158df4a",
+    "tarball": "https://registry.npmjs.org/accepts/-/accepts-1.1.4.tgz"
+  },
+  "engines": {
+    "node": ">= 0.8"
+  },
+  "files": [
+    "LICENSE",
+    "HISTORY.md",
+    "index.js"
+  ],
+  "gitHead": "df66414d80f096627b28f137127fce0a851d7900",
+  "homepage": "https://github.com/jshttp/accepts",
+  "keywords": [
+    "content",
+    "negotiation",
+    "accept",
+    "accepts"
+  ],
+  "license": "MIT",
+  "maintainers": [
+    {
+      "name": "jongleberry",
+      "email": "jonathanrichardong@gmail.com"
+    },
+    {
+      "name": "federomero",
+      "email": "federomero@gmail.com"
+    },
+    {
+      "name": "dougwilson",
+      "email": "doug@somethingdoug.com"
+    },
+    {
+      "name": "tjholowaychuk",
+      "email": "tj@vision-media.ca"
+    },
+    {
+      "name": "shtylman",
+      "email": "shtylman@gmail.com"
+    },
+    {
+      "name": "mscdex",
+      "email": "mscdex@mscdex.net"
+    },
+    {
+      "name": "fishrock123",
+      "email": "fishrock123@rocketmail.com"
+    }
+  ],
+  "name": "accepts",
+  "optionalDependencies": {},
+  "readme": "ERROR: No README data found!",
+  "repository": {
+    "type": "git",
+    "url": "git+https://github.com/jshttp/accepts.git"
+  },
+  "scripts": {
+    "test": "mocha --reporter spec --check-leaks --bail test/",
+    "test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --reporter dot --check-leaks test/",
+    "test-travis": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --reporter spec --check-leaks test/"
+  },
+  "version": "1.1.4"
+}
diff --git a/cep/node_modules/after/.npmignore b/cep/node_modules/after/.npmignore
new file mode 100644
index 0000000..6c78602
--- /dev/null
+++ b/cep/node_modules/after/.npmignore
@@ -0,0 +1,2 @@
+node_modules
+.monitor
diff --git a/cep/node_modules/after/.travis.yml b/cep/node_modules/after/.travis.yml
new file mode 100644
index 0000000..84fd7ca
--- /dev/null
+++ b/cep/node_modules/after/.travis.yml
@@ -0,0 +1,5 @@
+language: node_js
+node_js:
+  - 0.6
+  - 0.8
+  - 0.9
diff --git a/cep/node_modules/after/LICENCE b/cep/node_modules/after/LICENCE
new file mode 100644
index 0000000..7c35130
--- /dev/null
+++ b/cep/node_modules/after/LICENCE
@@ -0,0 +1,19 @@
+Copyright (c) 2011 Raynos.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
\ No newline at end of file
diff --git a/cep/node_modules/after/README.md b/cep/node_modules/after/README.md
new file mode 100644
index 0000000..0012d35
--- /dev/null
+++ b/cep/node_modules/after/README.md
@@ -0,0 +1,75 @@
+# After [![Build Status][1]][2]
+
+Invoke callback after n calls
+
+## Status: production ready
+
+## Example
+
+    var after = require("after")
+        , next = after(3, logItWorks)
+
+    next()
+    next()
+    next() // it works
+
+    function logItWorks() {
+        console.log("it works!")
+    }
+
+## Example with error handling
+
+    var after = require("after")
+        , next = after(3, logError)
+
+    next()
+    next(new Error("oops")) // logs oops
+    next() // does nothing
+
+    function logError(err) {
+        console.log(err)
+    }
+
+## After < 0.6.0
+
+Older versions of after had iterators and flows in them.
+
+These have been replaced with seperate modules
+
+ - [iterators][8]
+ - [composite][9]
+
+## Installation
+
+`npm install after`
+
+## Tests
+
+`npm test`
+
+## Blog post
+
+ - [Flow control in node.js][3]
+
+## Examples :
+
+ - [Determining the end of asynchronous operations][4]
+ - [In javascript what are best practices for executing multiple asynchronous functions][5]
+ - [JavaScript performance long running tasks][6]
+ - [Synchronous database queries with node.js][7]
+
+## Contributors
+
+ - Raynos
+
+## MIT Licenced
+
+  [1]: https://secure.travis-ci.org/Raynos/after.png
+  [2]: http://travis-ci.org/Raynos/after
+  [3]: http://raynos.org/blog/2/Flow-control-in-node.js
+  [4]: http://stackoverflow.com/questions/6852059/determining-the-end-of-asynchronous-operations-javascript/6852307#6852307
+  [5]: http://stackoverflow.com/questions/6869872/in-javascript-what-are-best-practices-for-executing-multiple-asynchronous-functi/6870031#6870031
+  [6]: http://stackoverflow.com/questions/6864397/javascript-performance-long-running-tasks/6889419#6889419
+  [7]: http://stackoverflow.com/questions/6597493/synchronous-database-queries-with-node-js/6620091#6620091
+  [8]: http://github.com/Raynos/iterators
+  [9]: http://github.com/Raynos/composite
diff --git a/cep/node_modules/after/index.js b/cep/node_modules/after/index.js
new file mode 100644
index 0000000..ec24879
--- /dev/null
+++ b/cep/node_modules/after/index.js
@@ -0,0 +1,28 @@
+module.exports = after
+
+function after(count, callback, err_cb) {
+    var bail = false
+    err_cb = err_cb || noop
+    proxy.count = count
+
+    return (count === 0) ? callback() : proxy
+
+    function proxy(err, result) {
+        if (proxy.count <= 0) {
+            throw new Error('after called too many times')
+        }
+        --proxy.count
+
+        // after first error, rest are passed to err_cb
+        if (err) {
+            bail = true
+            callback(err)
+            // future error callbacks will go to error handler
+            callback = err_cb
+        } else if (proxy.count === 0 && !bail) {
+            callback(null, result)
+        }
+    }
+}
+
+function noop() {}
diff --git a/cep/node_modules/after/package.json b/cep/node_modules/after/package.json
new file mode 100644
index 0000000..d48ea57
--- /dev/null
+++ b/cep/node_modules/after/package.json
@@ -0,0 +1,97 @@
+{
+  "_args": [
+    [
+      {
+        "raw": "after@0.8.1",
+        "scope": null,
+        "escapedName": "after",
+        "name": "after",
+        "rawSpec": "0.8.1",
+        "spec": "0.8.1",
+        "type": "version"
+      },
+      "D:\\repositories\\tk-framework-adobe\\extensions\\http_server\\node_modules\\engine.io-parser"
+    ]
+  ],
+  "_from": "after@0.8.1",
+  "_id": "after@0.8.1",
+  "_inCache": true,
+  "_installable": true,
+  "_location": "/after",
+  "_npmUser": {
+    "name": "raynos",
+    "email": "raynos2@gmail.com"
+  },
+  "_npmVersion": "1.2.25",
+  "_phantomChildren": {},
+  "_requested": {
+    "raw": "after@0.8.1",
+    "scope": null,
+    "escapedName": "after",
+    "name": "after",
+    "rawSpec": "0.8.1",
+    "spec": "0.8.1",
+    "type": "version"
+  },
+  "_requiredBy": [
+    "/engine.io-parser"
+  ],
+  "_resolved": "https://registry.npmjs.org/after/-/after-0.8.1.tgz",
+  "_shasum": "ab5d4fb883f596816d3515f8f791c0af486dd627",
+  "_shrinkwrap": null,
+  "_spec": "after@0.8.1",
+  "_where": "D:\\repositories\\tk-framework-adobe\\extensions\\http_server\\node_modules\\engine.io-parser",
+  "author": {
+    "name": "Raynos",
+    "email": "raynos2@gmail.com"
+  },
+  "bugs": {
+    "url": "https://github.com/Raynos/after/issues"
+  },
+  "contributors": [
+    {
+      "name": "Raynos",
+      "email": "raynos2@gmail.com",
+      "url": "http://raynos.org"
+    }
+  ],
+  "dependencies": {},
+  "description": "after - tiny flow control",
+  "devDependencies": {
+    "mocha": "~1.8.1"
+  },
+  "directories": {},
+  "dist": {
+    "shasum": "ab5d4fb883f596816d3515f8f791c0af486dd627",
+    "tarball": "https://registry.npmjs.org/after/-/after-0.8.1.tgz"
+  },
+  "homepage": "https://github.com/Raynos/after#readme",
+  "keywords": [
+    "flowcontrol",
+    "after",
+    "flow",
+    "control",
+    "arch"
+  ],
+  "maintainers": [
+    {
+      "name": "raynos",
+      "email": "raynos2@gmail.com"
+    },
+    {
+      "name": "shtylman",
+      "email": "shtylman@gmail.com"
+    }
+  ],
+  "name": "after",
+  "optionalDependencies": {},
+  "readme": "ERROR: No README data found!",
+  "repository": {
+    "type": "git",
+    "url": "git://github.com/Raynos/after.git"
+  },
+  "scripts": {
+    "test": "mocha --ui tdd --reporter spec test/*.js"
+  },
+  "version": "0.8.1"
+}
diff --git a/cep/node_modules/after/test/after-test.js b/cep/node_modules/after/test/after-test.js
new file mode 100644
index 0000000..0d63f4c
--- /dev/null
+++ b/cep/node_modules/after/test/after-test.js
@@ -0,0 +1,120 @@
+/*global suite, test*/
+
+var assert = require("assert")
+    , after = require("../")
+
+test("exists", function () {
+    assert(typeof after === "function", "after is not a function")
+})
+
+test("after when called with 0 invokes", function (done) {
+    after(0, done)
+});
+
+test("after 1", function (done) {
+    var next = after(1, done)
+    next()
+})
+
+test("after 5", function (done) {
+    var next = after(5, done)
+    , i = 5
+
+    while (i--) {
+        next()
+    }
+})
+
+test("manipulate count", function (done) {
+    var next = after(1, done)
+    , i = 5
+
+    next.count = i
+    while (i--) {
+        next()
+    }
+})
+
+test("after terminates on error", function (done) {
+    var next = after(2, function(err) {
+        assert.equal(err.message, 'test');
+        done();
+    })
+    next(new Error('test'))
+    next(new Error('test2'))
+})
+
+test('gee', function(done) {
+    done = after(2, done)
+
+    function cb(err) {
+        assert.equal(err.message, 1);
+        done()
+    }
+
+    var next = after(3, cb, function(err) {
+        assert.equal(err.message, 2)
+        done()
+    });
+
+    next()
+    next(new Error(1))
+    next(new Error(2))
+})
+
+test('eee', function(done) {
+    done = after(3, done)
+
+    function cb(err) {
+        assert.equal(err.message, 1);
+        done()
+    }
+
+    var next = after(3, cb, function(err) {
+        assert.equal(err.message, 2)
+        done()
+    });
+
+    next(new Error(1))
+    next(new Error(2))
+    next(new Error(2))
+})
+
+test('gge', function(done) {
+    function cb(err) {
+        assert.equal(err.message, 1);
+        done()
+    }
+
+    var next = after(3, cb, function(err) {
+        // should not happen
+        assert.ok(false);
+    });
+
+    next()
+    next()
+    next(new Error(1))
+})
+
+test('egg', function(done) {
+    function cb(err) {
+        assert.equal(err.message, 1);
+        done()
+    }
+
+    var next = after(3, cb, function(err) {
+        // should not happen
+        assert.ok(false);
+    });
+
+    next(new Error(1))
+    next()
+    next()
+})
+
+test('throws on too many calls', function(done) {
+    var next = after(1, done);
+    next()
+    assert.throws(next, /after called too many times/);
+});
+
diff --git a/cep/node_modules/arraybuffer.slice/.npmignore b/cep/node_modules/arraybuffer.slice/.npmignore
new file mode 100644
index 0000000..cfbee8d
--- /dev/null
+++ b/cep/node_modules/arraybuffer.slice/.npmignore
@@ -0,0 +1,17 @@
+lib-cov
+lcov.info
+*.seed
+*.log
+*.csv
+*.dat
+*.out
+*.pid
+*.gz
+
+pids
+logs
+results
+build
+.grunt
+
+node_modules
diff --git a/cep/node_modules/arraybuffer.slice/Makefile b/cep/node_modules/arraybuffer.slice/Makefile
new file mode 100644
index 0000000..849887f
--- /dev/null
+++ b/cep/node_modules/arraybuffer.slice/Makefile
@@ -0,0 +1,8 @@
+
+REPORTER = dot
+
+test:
+	@./node_modules/.bin/mocha \
+		--reporter $(REPORTER)
+
+.PHONY: test
diff --git a/cep/node_modules/arraybuffer.slice/README.md b/cep/node_modules/arraybuffer.slice/README.md
new file mode 100644
index 0000000..15e465e
--- /dev/null
+++ b/cep/node_modules/arraybuffer.slice/README.md
@@ -0,0 +1,17 @@
+# How to
+```javascript
+var sliceBuffer = require('arraybuffer.slice');
+var ab = (new Int8Array(5)).buffer;
+var sliced = sliceBuffer(ab, 1, 3);
+sliced = sliceBuffer(ab, 1);
+```
+
+# Licence (MIT)
+Copyright (C) 2013 Rase-
+
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/cep/node_modules/arraybuffer.slice/index.js b/cep/node_modules/arraybuffer.slice/index.js
new file mode 100644
index 0000000..11ac556
--- /dev/null
+++ b/cep/node_modules/arraybuffer.slice/index.js
@@ -0,0 +1,29 @@
+/**
+ * An abstraction for slicing an arraybuffer even when
+ * ArrayBuffer.prototype.slice is not supported
+ *
+ * @api public
+ */
+
+module.exports = function(arraybuffer, start, end) {
+  var bytes = arraybuffer.byteLength;
+  start = start || 0;
+  end = end || bytes;
+
+  if (arraybuffer.slice) { return arraybuffer.slice(start, end); }
+
+  if (start < 0) { start += bytes; }
+  if (end < 0) { end += bytes; }
+  if (end > bytes) { end = bytes; }
+
+  if (start >= bytes || start >= end || bytes === 0) {
+    return new ArrayBuffer(0);
+  }
+
+  var abv = new Uint8Array(arraybuffer);
+  var result = new Uint8Array(end - start);
+  for (var i = start, ii = 0; i < end; i++, ii++) {
+    result[ii] = abv[i];
+  }
+  return result.buffer;
+};
diff --git a/cep/node_modules/arraybuffer.slice/package.json b/cep/node_modules/arraybuffer.slice/package.json
new file mode 100644
index 0000000..672e6d6
--- /dev/null
+++ b/cep/node_modules/arraybuffer.slice/package.json
@@ -0,0 +1,73 @@
+{
+  "_args": [
+    [
+      {
+        "raw": "arraybuffer.slice@0.0.6",
+        "scope": null,
+        "escapedName": "arraybuffer.slice",
+        "name": "arraybuffer.slice",
+        "rawSpec": "0.0.6",
+        "spec": "0.0.6",
+        "type": "version"
+      },
+      "D:\\repositories\\tk-framework-adobe\\extensions\\http_server\\node_modules\\engine.io-parser"
+    ]
+  ],
+  "_from": "arraybuffer.slice@0.0.6",
+  "_id": "arraybuffer.slice@0.0.6",
+  "_inCache": true,
+  "_installable": true,
+  "_location": "/arraybuffer.slice",
+  "_npmUser": {
+    "name": "rase-",
+    "email": "tonykovanen@hotmail.com"
+  },
+  "_npmVersion": "1.3.5",
+  "_phantomChildren": {},
+  "_requested": {
+    "raw": "arraybuffer.slice@0.0.6",
+    "scope": null,
+    "escapedName": "arraybuffer.slice",
+    "name": "arraybuffer.slice",
+    "rawSpec": "0.0.6",
+    "spec": "0.0.6",
+    "type": "version"
+  },
+  "_requiredBy": [
+    "/engine.io-parser"
+  ],
+  "_resolved": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.6.tgz",
+  "_shasum": "f33b2159f0532a3f3107a272c0ccfbd1ad2979ca",
+  "_shrinkwrap": null,
+  "_spec": "arraybuffer.slice@0.0.6",
+  "_where": "D:\\repositories\\tk-framework-adobe\\extensions\\http_server\\node_modules\\engine.io-parser",
+  "bugs": {
+    "url": "https://github.com/rase-/arraybuffer.slice/issues"
+  },
+  "dependencies": {},
+  "description": "Exports a function for slicing ArrayBuffers (no polyfilling)",
+  "devDependencies": {
+    "expect.js": "0.2.0",
+    "mocha": "1.17.1"
+  },
+  "directories": {},
+  "dist": {
+    "shasum": "f33b2159f0532a3f3107a272c0ccfbd1ad2979ca",
+    "tarball": "https://registry.npmjs.org/arraybuffer.slice/-/arraybuffer.slice-0.0.6.tgz"
+  },
+  "homepage": "https://github.com/rase-/arraybuffer.slice",
+  "maintainers": [
+    {
+      "name": "rase-",
+      "email": "tonykovanen@hotmail.com"
+    }
+  ],
+  "name": "arraybuffer.slice",
+  "optionalDependencies": {},
+  "readme": "ERROR: No README data found!",
+  "repository": {
+    "type": "git",
+    "url": "git+ssh://git@github.com/rase-/arraybuffer.slice.git"
+  },
+  "version": "0.0.6"
+}
diff --git a/cep/node_modules/arraybuffer.slice/test/slice-buffer.js b/cep/node_modules/arraybuffer.slice/test/slice-buffer.js
new file mode 100644
index 0000000..4778da6
--- /dev/null
+++ b/cep/node_modules/arraybuffer.slice/test/slice-buffer.js
@@ -0,0 +1,227 @@
+/*
+ * Test dependencies
+ */
+
+var sliceBuffer = require('../index.js');
+var expect = require('expect.js');
+
+/**
+ * Tests
+ */
+
+describe('sliceBuffer', function() {
+  describe('using standard slice', function() {
+    it('should slice correctly with only start provided', function() {
+      var abv = new Uint8Array(10);
+      for (var i = 0; i < abv.length; i++) {
+        abv[i] = i;
+      }
+
+      var sliced = sliceBuffer(abv.buffer, 3);
+      var sabv = new Uint8Array(sliced);
+      for (var i = 3, ii = 0; i < abv.length; i++, ii++) {
+        expect(abv[i]).to.equal(sabv[ii]);
+      }
+    });
+
+    it('should slice correctly with start and end provided', function() {
+      var abv = new Uint8Array(10);
+      for (var i = 0; i < abv.length; i++) {
+        abv[i] = i;
+      }
+
+      var sliced = sliceBuffer(abv.buffer, 3, 8);
+      var sabv = new Uint8Array(sliced);
+      for (var i = 3, ii = 0; i < 8; i++, ii++) {
+        expect(abv[i]).to.equal(sabv[ii]);
+      }
+    });
+
+    it('should slice correctly with negative start', function() {
+      var abv = new Uint8Array(10);
+      for (var i = 0; i < abv.length; i++) {
+        abv[i] = i;
+      }
+
+      var sliced = sliceBuffer(abv.buffer, -3);
+      var sabv = new Uint8Array(sliced);
+      for (var i = abv.length - 3, ii = 0; i < abv.length; i++, ii++) {
+        expect(abv[i]).to.equal(sabv[ii]);
+      }
+    });
+
+    it('should slice correctly with negative end', function() {
+      var abv = new Uint8Array(10);
+      for (var i = 0; i < abv.length; i++) {
+        abv[i] = i;
+      }
+
+      var sliced = sliceBuffer(abv.buffer, 0, -3);
+      var sabv = new Uint8Array(sliced);
+      for (var i = 0, ii = 0; i < abv.length - 3; i++, ii++) {
+        expect(abv[i]).to.equal(sabv[ii]);
+      }
+    });
+
+    it('should slice correctly with negative start and end', function() {
+      var abv = new Uint8Array(10);
+      for (var i = 0; i < abv.length; i++) {
+        abv[i] = i;
+      }
+
+      var sliced = sliceBuffer(abv.buffer, -6, -3);
+      var sabv = new Uint8Array(sliced);
+      for (var i = abv.length - 6, ii = 0; i < abv.length - 3; i++, ii++) {
+        expect(abv[i]).to.equal(sabv[ii]);
+      }
+    });
+
+    it('should slice correctly with equal start and end', function() {
+      var abv = new Uint8Array(10);
+      for (var i = 0; i < abv.length; i++) {
+        abv[i] = i;
+      }
+
+      var sliced = sliceBuffer(abv.buffer, 1, 1);
+      expect(sliced.byteLength).to.equal(0);
+    });
+    
+    it('should slice correctly when end larger than buffer', function() {
+      var abv = new Uint8Array(10);
+      for (var i = 0; i < abv.length; i++) {
+        abv[i] = i;
+      }
+
+      var sliced = sliceBuffer(abv.buffer, 0, 100);
+      expect(new Uint8Array(sliced)).to.eql(abv);
+    });
+
+    it('shoud slice correctly when start larger than end', function() {
+      var abv = new Uint8Array(10);
+      for (var i = 0; i < abv.length; i++) {
+        abv[i] = i;
+      }
+
+      var sliced = sliceBuffer(abv.buffer, 6, 5);
+      expect(sliced.byteLength).to.equal(0);
+    });
+  });
+
+  describe('using fallback', function() {
+    it('should slice correctly with only start provided', function() {
+      var abv = new Uint8Array(10);
+      for (var i = 0; i < abv.length; i++) {
+        abv[i] = i;
+      }
+      var ab = abv.buffer;
+      ab.slice = undefined;
+
+      var sliced = sliceBuffer(ab, 3);
+      var sabv = new Uint8Array(sliced);
+      for (var i = 3, ii = 0; i < abv.length; i++, ii++) {
+        expect(abv[i]).to.equal(sabv[ii]);
+      }
+    });
+
+    it('should slice correctly with start and end provided', function() {
+      var abv = new Uint8Array(10);
+      for (var i = 0; i < abv.length; i++) {
+        abv[i] = i;
+      }
+      var ab = abv.buffer;
+      ab.slice = undefined;
+
+
+      var sliced = sliceBuffer(ab, 3, 8);
+      var sabv = new Uint8Array(sliced);
+      for (var i = 3, ii = 0; i < 8; i++, ii++) {
+        expect(abv[i]).to.equal(sabv[ii]);
+      }
+    });
+
+    it('should slice correctly with negative start', function() {
+      var abv = new Uint8Array(10);
+      for (var i = 0; i < abv.length; i++) {
+        abv[i] = i;
+      }
+      var ab = abv.buffer;
+      ab.slice = undefined;
+
+
+      var sliced = sliceBuffer(ab, -3);
+      var sabv = new Uint8Array(sliced);
+      for (var i = abv.length - 3, ii = 0; i < abv.length; i++, ii++) {
+        expect(abv[i]).to.equal(sabv[ii]);
+      }
+    });
+
+    it('should slice correctly with negative end', function() {
+      var abv = new Uint8Array(10);
+      for (var i = 0; i < abv.length; i++) {
+        abv[i] = i;
+      }
+      var ab = abv.buffer;
+      ab.slice = undefined;
+
+      var sliced = sliceBuffer(ab, 0, -3);
+      var sabv = new Uint8Array(sliced);
+      for (var i = 0, ii = 0; i < abv.length - 3; i++, ii++) {
+        expect(abv[i]).to.equal(sabv[ii]);
+      }
+    });
+
+    it('should slice correctly with negative start and end', function() {
+      var abv = new Uint8Array(10);
+      for (var i = 0; i < abv.length; i++) {
+        abv[i] = i;
+      }
+      var ab = abv.buffer;
+      ab.slice = undefined;
+
+      var sliced = sliceBuffer(ab, -6, -3);
+      var sabv = new Uint8Array(sliced);
+      for (var i = abv.length - 6, ii = 0; i < abv.length - 3; i++, ii++) {
+        expect(abv[i]).to.equal(sabv[ii]);
+      }
+    });
+
+    it('should slice correctly with equal start and end', function() {
+      var abv = new Uint8Array(10);
+      for (var i = 0; i < abv.length; i++) {
+        abv[i] = i;
+      }
+      var ab = abv.buffer;
+      ab.slice = undefined;
+
+      var sliced = sliceBuffer(ab, 1, 1);
+      expect(sliced.byteLength).to.equal(0);
+    });
+
+    it('should slice correctly when end larger than buffer', function() {
+      var abv = new Uint8Array(10);
+      for (var i = 0; i < abv.length; i++) {
+        abv[i] = i;
+      }
+      var ab = abv.buffer;
+      ab.slice = undefined;
+
+      var sliced = sliceBuffer(ab, 0, 100);
+      var sabv = new Uint8Array(sliced);
+      for (var i = 0; i < abv.length; i++) {
+        expect(abv[i]).to.equal(sabv[i]);
+      }
+    });
+
+    it('shoud slice correctly when start larger than end', function() {
+      var abv = new Uint8Array(10);
+      for (var i = 0; i < abv.length; i++) {
+        abv[i] = i;
+      }
+      var ab = abv.buffer;
+      ab.slice = undefined;
+
+      var sliced = sliceBuffer(ab, 6, 5);
+      expect(sliced.byteLength).to.equal(0);
+    });
+  });
+});
diff --git a/cep/node_modules/backo2/.npmignore b/cep/node_modules/backo2/.npmignore
new file mode 100644
index 0000000..c2658d7
--- /dev/null
+++ b/cep/node_modules/backo2/.npmignore
@@ -0,0 +1 @@
+node_modules/
diff --git a/cep/node_modules/backo2/History.md b/cep/node_modules/backo2/History.md
new file mode 100644
index 0000000..8eb28b8
--- /dev/null
+++ b/cep/node_modules/backo2/History.md
@@ -0,0 +1,12 @@
+
+1.0.1 / 2014-02-17
+==================
+
+ * go away decimal point
+ * history
+
+1.0.0 / 2014-02-17
+==================
+
+ * add jitter option
+ * Initial commit
diff --git a/cep/node_modules/backo2/Makefile b/cep/node_modules/backo2/Makefile
new file mode 100644
index 0000000..9987df8
--- /dev/null
+++ b/cep/node_modules/backo2/Makefile
@@ -0,0 +1,8 @@
+
+test:
+	@./node_modules/.bin/mocha \
+		--require should \
+		--reporter dot \
+		--bail
+
+.PHONY: test
\ No newline at end of file
diff --git a/cep/node_modules/backo2/Readme.md b/cep/node_modules/backo2/Readme.md
new file mode 100644
index 0000000..0df2a39
--- /dev/null
+++ b/cep/node_modules/backo2/Readme.md
@@ -0,0 +1,34 @@
+# backo
+
+  Simple exponential backoff because the others seem to have weird abstractions.
+
+## Installation
+
+```
+$ npm install backo
+```
+
+## Options
+
+ - `min` initial timeout in milliseconds [100]
+ - `max` max timeout [10000]
+ - `jitter` [0]
+ - `factor` [2]
+
+## Example
+
+```js
+var Backoff = require('backo');
+var backoff = new Backoff({ min: 100, max: 20000 });
+
+setTimeout(function(){
+  something.reconnect();
+}, backoff.duration());
+
+// later when something works
+backoff.reset()
+```
+
+# License
+
+  MIT
diff --git a/cep/node_modules/backo2/component.json b/cep/node_modules/backo2/component.json
new file mode 100644
index 0000000..994845a
--- /dev/null
+++ b/cep/node_modules/backo2/component.json
@@ -0,0 +1,11 @@
+{
+  "name": "backo",
+  "repo": "segmentio/backo",
+  "dependencies": {},
+  "version": "1.0.1",
+  "description": "simple backoff without the weird abstractions",
+  "keywords": ["backoff"],
+  "license": "MIT",
+  "scripts": ["index.js"],
+  "main": "index.js"
+}
diff --git a/cep/node_modules/backo2/index.js b/cep/node_modules/backo2/index.js
new file mode 100644
index 0000000..fac4429
--- /dev/null
+++ b/cep/node_modules/backo2/index.js
@@ -0,0 +1,85 @@
+
+/**
+ * Expose `Backoff`.
+ */
+
+module.exports = Backoff;
+
+/**
+ * Initialize backoff timer with `opts`.
+ *
+ * - `min` initial timeout in milliseconds [100]
+ * - `max` max timeout [10000]
+ * - `jitter` [0]
+ * - `factor` [2]
+ *
+ * @param {Object} opts
+ * @api public
+ */
+
+function Backoff(opts) {
+  opts = opts || {};
+  this.ms = opts.min || 100;
+  this.max = opts.max || 10000;
+  this.factor = opts.factor || 2;
+  this.jitter = opts.jitter > 0 && opts.jitter <= 1 ? opts.jitter : 0;
+  this.attempts = 0;
+}
+
+/**
+ * Return the backoff duration.
+ *
+ * @return {Number}
+ * @api public
+ */
+
+Backoff.prototype.duration = function(){
+  var ms = this.ms * Math.pow(this.factor, this.attempts++);
+  if (this.jitter) {
+    var rand =  Math.random();
+    var deviation = Math.floor(rand * this.jitter * ms);
+    ms = (Math.floor(rand * 10) & 1) == 0  ? ms - deviation : ms + deviation;
+  }
+  return Math.min(ms, this.max) | 0;
+};
+
+/**
+ * Reset the number of attempts.
+ *
+ * @api public
+ */
+
+Backoff.prototype.reset = function(){
+  this.attempts = 0;
+};
+
+/**
+ * Set the minimum duration
+ *
+ * @api public
+ */
+
+Backoff.prototype.setMin = function(min){
+  this.ms = min;
+};
+
+/**
+ * Set the maximum duration
+ *
+ * @api public
+ */
+
+Backoff.prototype.setMax = function(max){
+  this.max = max;
+};
+
+/**
+ * Set the jitter
+ *
+ * @api public
+ */
+
+Backoff.prototype.setJitter = function(jitter){
+  this.jitter = jitter;
+};
+
diff --git a/cep/node_modules/backo2/package.json b/cep/node_modules/backo2/package.json
new file mode 100644
index 0000000..2d92e21
--- /dev/null
+++ b/cep/node_modules/backo2/package.json
@@ -0,0 +1,79 @@
+{
+  "_args": [
+    [
+      {
+        "raw": "backo2@1.0.2",
+        "scope": null,
+        "escapedName": "backo2",
+        "name": "backo2",
+        "rawSpec": "1.0.2",
+        "spec": "1.0.2",
+        "type": "version"
+      },
+      "D:\\repositories\\tk-framework-adobe\\extensions\\http_server\\node_modules\\socket.io-client"
+    ]
+  ],
+  "_from": "backo2@1.0.2",
+  "_id": "backo2@1.0.2",
+  "_inCache": true,
+  "_installable": true,
+  "_location": "/backo2",
+  "_npmUser": {
+    "name": "mokesmokes",
+    "email": "mokesmokes@gmail.com"
+  },
+  "_npmVersion": "1.4.28",
+  "_phantomChildren": {},
+  "_requested": {
+    "raw": "backo2@1.0.2",
+    "scope": null,
+    "escapedName": "backo2",
+    "name": "backo2",
+    "rawSpec": "1.0.2",
+    "spec": "1.0.2",
+    "type": "version"
+  },
+  "_requiredBy": [
+    "/socket.io-client"
+  ],
+  "_resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz",
+  "_shasum": "31ab1ac8b129363463e35b3ebb69f4dfcfba7947",
+  "_shrinkwrap": null,
+  "_spec": "backo2@1.0.2",
+  "_where": "D:\\repositories\\tk-framework-adobe\\extensions\\http_server\\node_modules\\socket.io-client",
+  "bugs": {
+    "url": "https://github.com/mokesmokes/backo/issues"
+  },
+  "dependencies": {},
+  "description": "simple backoff based on segmentio/backo",
+  "devDependencies": {
+    "mocha": "*",
+    "should": "*"
+  },
+  "directories": {},
+  "dist": {
+    "shasum": "31ab1ac8b129363463e35b3ebb69f4dfcfba7947",
+    "tarball": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz"
+  },
+  "gitHead": "3e695bade7756fef2295e8883bf3570a06e5d9ec",
+  "homepage": "https://github.com/mokesmokes/backo",
+  "keywords": [
+    "backoff"
+  ],
+  "license": "MIT",
+  "maintainers": [
+    {
+      "name": "mokesmokes",
+      "email": "mokesmokes@gmail.com"
+    }
+  ],
+  "name": "backo2",
+  "optionalDependencies": {},
+  "readme": "ERROR: No README data found!",
+  "repository": {
+    "type": "git",
+    "url": "git+https://github.com/mokesmokes/backo.git"
+  },
+  "scripts": {},
+  "version": "1.0.2"
+}
diff --git a/cep/node_modules/backo2/test/index.js b/cep/node_modules/backo2/test/index.js
new file mode 100644
index 0000000..ea1f6de
--- /dev/null
+++ b/cep/node_modules/backo2/test/index.js
@@ -0,0 +1,18 @@
+
+var Backoff = require('..');
+var assert = require('assert');
+
+describe('.duration()', function(){
+  it('should increase the backoff', function(){
+    var b = new Backoff;
+
+    assert(100 == b.duration());
+    assert(200 == b.duration());
+    assert(400 == b.duration());
+    assert(800 == b.duration());
+
+    b.reset();
+    assert(100 == b.duration());
+    assert(200 == b.duration());
+  })
+})
\ No newline at end of file
diff --git a/cep/node_modules/base64-arraybuffer/.npmignore b/cep/node_modules/base64-arraybuffer/.npmignore
new file mode 100644
index 0000000..2ccbe46
--- /dev/null
+++ b/cep/node_modules/base64-arraybuffer/.npmignore
@@ -0,0 +1 @@
+/node_modules/
diff --git a/cep/node_modules/base64-arraybuffer/.travis.yml b/cep/node_modules/base64-arraybuffer/.travis.yml
new file mode 100644
index 0000000..e2eeb99
--- /dev/null
+++ b/cep/node_modules/base64-arraybuffer/.travis.yml
@@ -0,0 +1,6 @@
+---
+language: node_js
+node_js:
+- '0.10'
+before_script:
+- npm install
diff --git a/cep/node_modules/base64-arraybuffer/LICENSE-MIT b/cep/node_modules/base64-arraybuffer/LICENSE-MIT
new file mode 100644
index 0000000..ed27b41
--- /dev/null
+++ b/cep/node_modules/base64-arraybuffer/LICENSE-MIT
@@ -0,0 +1,22 @@
+Copyright (c) 2012 Niklas von Hertzen
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
diff --git a/cep/node_modules/base64-arraybuffer/README.md b/cep/node_modules/base64-arraybuffer/README.md
new file mode 100644
index 0000000..b841154
--- /dev/null
+++ b/cep/node_modules/base64-arraybuffer/README.md
@@ -0,0 +1,23 @@
+# base64-arraybuffer
+
+[![Build Status](https://travis-ci.org/niklasvh/base64-arraybuffer.png)](https://travis-ci.org/niklasvh/base64-arraybuffer)
+
+Encode/decode base64 data into ArrayBuffers
+
+## Getting Started
+Install the module with: `npm install base64-arraybuffer`
+
+## API
+The library encodes and decodes base64 to and from ArrayBuffers
+
+ - __encode(buffer)__ - Encodes `ArrayBuffer` into base64 string
+ - __decode(str)__ - Decodes base64 string to `ArrayBuffer`
+
+## Release History
+
+ - 0.1.2 - Fix old format of typed arrays
+ - 0.1.0 - Initial version, basic decode/encode base64 to and from ArrayBuffer
+
+## License
+Copyright (c) 2012 Niklas von Hertzen
+Licensed under the MIT license.
diff --git a/cep/node_modules/base64-arraybuffer/grunt.js b/cep/node_modules/base64-arraybuffer/grunt.js
new file mode 100644
index 0000000..73c4e7a
--- /dev/null
+++ b/cep/node_modules/base64-arraybuffer/grunt.js
@@ -0,0 +1,39 @@
+module.exports = function(grunt) {
+  "use strict";
+  // Project configuration.
+  grunt.initConfig({
+    pkg: '',
+    test: {
+      files: ['test/**/*.js']
+    },
+    lint: {
+      files: ['grunt.js', 'lib/**/*.js', 'test/**/*.js']
+    },
+    watch: {
+      files: '',
+      tasks: 'default'
+    },
+    jshint: {
+      options: {
+        curly: true,
+        eqeqeq: true,
+        immed: true,
+        latedef: true,
+        newcap: true,
+        noarg: true,
+        sub: true,
+        undef: true,
+        boss: true,
+        eqnull: true,
+        node: true
+      },
+      globals: {
+        exports: true
+      }
+    }
+  });
+
+  // Default task.
+  grunt.registerTask('default', 'test');
+
+};
\ No newline at end of file
diff --git a/cep/node_modules/base64-arraybuffer/lib/base64-arraybuffer.js b/cep/node_modules/base64-arraybuffer/lib/base64-arraybuffer.js
new file mode 100644
index 0000000..362fbfa
--- /dev/null
+++ b/cep/node_modules/base64-arraybuffer/lib/base64-arraybuffer.js
@@ -0,0 +1,59 @@
+/*
+ * base64-arraybuffer
+ * https://github.com/niklasvh/base64-arraybuffer
+ *
+ * Copyright (c) 2012 Niklas von Hertzen
+ * Licensed under the MIT license.
+ */
+(function(chars){
+  "use strict";
+
+  exports.encode = function(arraybuffer) {
+    var bytes = new Uint8Array(arraybuffer),
+    i, len = bytes.length, base64 = "";
+
+    for (i = 0; i < len; i+=3) {
+      base64 += chars[bytes[i] >> 2];
+      base64 += chars[((bytes[i] & 3) << 4) | (bytes[i + 1] >> 4)];
+      base64 += chars[((bytes[i + 1] & 15) << 2) | (bytes[i + 2] >> 6)];
+      base64 += chars[bytes[i + 2] & 63];
+    }
+
+    if ((len % 3) === 2) {
+      base64 = base64.substring(0, base64.length - 1) + "=";
+    } else if (len % 3 === 1) {
+      base64 = base64.substring(0, base64.length - 2) + "==";
+    }
+
+    return base64;
+  };
+
+  exports.decode =  function(base64) {
+    var bufferLength = base64.length * 0.75,
+    len = base64.length, i, p = 0,
+    encoded1, encoded2, encoded3, encoded4;
+
+    if (base64[base64.length - 1] === "=") {
+      bufferLength--;
+      if (base64[base64.length - 2] === "=") {
+        bufferLength--;
+      }
+    }
+
+    var arraybuffer = new ArrayBuffer(bufferLength),
+    bytes = new Uint8Array(arraybuffer);
+
+    for (i = 0; i < len; i+=4) {
+      encoded1 = chars.indexOf(base64[i]);
+      encoded2 = chars.indexOf(base64[i+1]);
+      encoded3 = chars.indexOf(base64[i+2]);
+      encoded4 = chars.indexOf(base64[i+3]);
+
+      bytes[p++] = (encoded1 << 2) | (encoded2 >> 4);
+      bytes[p++] = ((encoded2 & 15) << 4) | (encoded3 >> 2);
+      bytes[p++] = ((encoded3 & 3) << 6) | (encoded4 & 63);
+    }
+
+    return arraybuffer;
+  };
+})("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/");
diff --git a/cep/node_modules/base64-arraybuffer/package.json b/cep/node_modules/base64-arraybuffer/package.json
new file mode 100644
index 0000000..14c6df8
--- /dev/null
+++ b/cep/node_modules/base64-arraybuffer/package.json
@@ -0,0 +1,91 @@
+{
+  "_args": [
+    [
+      {
+        "raw": "base64-arraybuffer@0.1.2",
+        "scope": null,
+        "escapedName": "base64-arraybuffer",
+        "name": "base64-arraybuffer",
+        "rawSpec": "0.1.2",
+        "spec": "0.1.2",
+        "type": "version"
+      },
+      "D:\\repositories\\tk-framework-adobe\\extensions\\http_server\\node_modules\\engine.io-parser"
+    ]
+  ],
+  "_from": "base64-arraybuffer@0.1.2",
+  "_id": "base64-arraybuffer@0.1.2",
+  "_inCache": true,
+  "_installable": true,
+  "_location": "/base64-arraybuffer",
+  "_npmUser": {
+    "name": "niklasvh",
+    "email": "niklasvh@gmail.com"
+  },
+  "_npmVersion": "1.3.21",
+  "_phantomChildren": {},
+  "_requested": {
+    "raw": "base64-arraybuffer@0.1.2",
+    "scope": null,
+    "escapedName": "base64-arraybuffer",
+    "name": "base64-arraybuffer",
+    "rawSpec": "0.1.2",
+    "spec": "0.1.2",
+    "type": "version"
+  },
+  "_requiredBy": [
+    "/engine.io-parser"
+  ],
+  "_resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.2.tgz",
+  "_shasum": "474df4a9f2da24e05df3158c3b1db3c3cd46a154",
+  "_shrinkwrap": null,
+  "_spec": "base64-arraybuffer@0.1.2",
+  "_where": "D:\\repositories\\tk-framework-adobe\\extensions\\http_server\\node_modules\\engine.io-parser",
+  "author": {
+    "name": "Niklas von Hertzen",
+    "email": "niklasvh@gmail.com",
+    "url": "http://hertzen.com"
+  },
+  "bugs": {
+    "url": "https://github.com/niklasvh/base64-arraybuffer/issues"
+  },
+  "dependencies": {},
+  "description": "Encode/decode base64 data into ArrayBuffers",
+  "devDependencies": {
+    "grunt": "~0.3.17"
+  },
+  "directories": {},
+  "dist": {
+    "shasum": "474df4a9f2da24e05df3158c3b1db3c3cd46a154",
+    "tarball": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-0.1.2.tgz"
+  },
+  "engines": {
+    "node": ">= 0.6.0"
+  },
+  "homepage": "https://github.com/niklasvh/base64-arraybuffer",
+  "keywords": [],
+  "licenses": [
+    {
+      "type": "MIT",
+      "url": "https://github.com/niklasvh/base64-arraybuffer/blob/master/LICENSE-MIT"
+    }
+  ],
+  "main": "lib/base64-arraybuffer",
+  "maintainers": [
+    {
+      "name": "niklasvh",
+      "email": "niklasvh@gmail.com"
+    }
+  ],
+  "name": "base64-arraybuffer",
+  "optionalDependencies": {},
+  "readme": "ERROR: No README data found!",
+  "repository": {
+    "type": "git",
+    "url": "git+https://github.com/niklasvh/base64-arraybuffer.git"
+  },
+  "scripts": {
+    "test": "grunt test"
+  },
+  "version": "0.1.2"
+}
diff --git a/cep/node_modules/base64-arraybuffer/test/base64-arraybuffer_test.js b/cep/node_modules/base64-arraybuffer/test/base64-arraybuffer_test.js
new file mode 100644
index 0000000..fe89c22
--- /dev/null
+++ b/cep/node_modules/base64-arraybuffer/test/base64-arraybuffer_test.js
@@ -0,0 +1,72 @@
+(function(){
+  "use strict";
+  var base64_arraybuffer = require('../lib/base64-arraybuffer.js');
+
+  /*
+  ======== A Handy Little Nodeunit Reference ========
+  https://github.com/caolan/nodeunit
+
+  Test methods:
+    test.expect(numAssertions)
+    test.done()
+  Test assertions:
+    test.ok(value, [message])
+    test.equal(actual, expected, [message])
+    test.notEqual(actual, expected, [message])
+    test.deepEqual(actual, expected, [message])
+    test.notDeepEqual(actual, expected, [message])
+    test.strictEqual(actual, expected, [message])
+    test.notStrictEqual(actual, expected, [message])
+    test.throws(block, [error], [message])
+    test.doesNotThrow(block, [error], [message])
+    test.ifError(value)
+*/
+
+
+  function stringArrayBuffer(str) {
+    var buffer = new ArrayBuffer(str.length);
+    var bytes = new Uint8Array(buffer);
+
+    str.split('').forEach(function(str, i) {
+      bytes[i] = str.charCodeAt(0);
+    });
+
+    return buffer;
+  }
+
+  function testArrayBuffers(buffer1, buffer2) {
+    var len1 = buffer1.byteLength,
+    len2 = buffer2.byteLength;
+    if (len1 !== len2) {
+      console.log(buffer1, buffer2);
+      return false;
+    }
+
+    for (var i = 0; i < len1; i++) {
+      if (buffer1[i] !== buffer1[i]) {
+        console.log(i, buffer1, buffer2);
+        return false;
+      }
+    }
+    return true;
+  }
+
+  exports['base64tests'] = {
+    'encode': function(test) {
+      test.expect(4);
+
+      test.equal(base64_arraybuffer.encode(stringArrayBuffer("Hello world")), "SGVsbG8gd29ybGQ=", 'encode "Hello world"');
+      test.equal(base64_arraybuffer.encode(stringArrayBuffer("Man")), 'TWFu', 'encode "Man"');
+      test.equal(base64_arraybuffer.encode(stringArrayBuffer("Ma")), "TWE=", 'encode "Ma"');
+      test.equal(base64_arraybuffer.encode(stringArrayBuffer("Hello worlds!")), "SGVsbG8gd29ybGRzIQ==", 'encode "Hello worlds!"');
+      test.done();
+    },
+    'decode': function(test) {
+      test.expect(3);
+      test.ok(testArrayBuffers(base64_arraybuffer.decode("TWFu"), stringArrayBuffer("Man")), 'decode "Man"');
+      test.ok(testArrayBuffers(base64_arraybuffer.decode("SGVsbG8gd29ybGQ="), stringArrayBuffer("Hello world")), 'decode "Hello world"');
+      test.ok(testArrayBuffers(base64_arraybuffer.decode("SGVsbG8gd29ybGRzIQ=="), stringArrayBuffer("Hello worlds!")), 'decode "Hello worlds!"');
+      test.done();
+    }
+  };
+})();
diff --git a/cep/node_modules/base64id/.npmignore b/cep/node_modules/base64id/.npmignore
new file mode 100644
index 0000000..39e9864
--- /dev/null
+++ b/cep/node_modules/base64id/.npmignore
@@ -0,0 +1,3 @@
+support
+test
+examples
diff --git a/cep/node_modules/base64id/README.md b/cep/node_modules/base64id/README.md
new file mode 100644
index 0000000..b4361c1
--- /dev/null
+++ b/cep/node_modules/base64id/README.md
@@ -0,0 +1,18 @@
+base64id
+========
+
+Node.js module that generates a base64 id.
+
+Uses crypto.randomBytes when available, falls back to unsafe methods for node.js <= 0.4.
+
+To increase performance, random bytes are buffered to minimize the number of synchronous calls to crypto.randomBytes.
+
+## Installation
+
+   $ npm install mongoose
+
+## Usage
+
+   var base64id = require('base64id');
+
+   var id = base64id.generateId();
diff --git a/cep/node_modules/base64id/lib/base64id.js b/cep/node_modules/base64id/lib/base64id.js
new file mode 100644
index 0000000..f688159
--- /dev/null
+++ b/cep/node_modules/base64id/lib/base64id.js
@@ -0,0 +1,103 @@
+/*!
+ * base64id v0.1.0
+ */
+
+/**
+ * Module dependencies
+ */
+
+var crypto = require('crypto');
+
+/**
+ * Constructor
+ */
+
+var Base64Id = function() { };
+
+/**
+ * Get random bytes
+ *
+ * Uses a buffer if available, falls back to crypto.randomBytes
+ */
+
+Base64Id.prototype.getRandomBytes = function(bytes) {
+
+  var BUFFER_SIZE = 4096
+  var self = this;  
+  
+  bytes = bytes || 12;
+
+  if (bytes > BUFFER_SIZE) {
+    return crypto.randomBytes(bytes);
+  }
+  
+  var bytesInBuffer = parseInt(BUFFER_SIZE/bytes);
+  var threshold = parseInt(bytesInBuffer*0.85);
+
+  if (!threshold) {
+    return crypto.randomBytes(bytes);
+  }
+
+  if (this.bytesBufferIndex == null) {
+     this.bytesBufferIndex = -1;
+  }
+
+  if (this.bytesBufferIndex == bytesInBuffer) {
+    this.bytesBuffer = null;
+    this.bytesBufferIndex = -1;
+  }
+
+  // No buffered bytes available or index above threshold
+  if (this.bytesBufferIndex == -1 || this.bytesBufferIndex > threshold) {
+     
+    if (!this.isGeneratingBytes) {
+      this.isGeneratingBytes = true;
+      crypto.randomBytes(BUFFER_SIZE, function(err, bytes) {
+        self.bytesBuffer = bytes;
+        self.bytesBufferIndex = 0;
+        self.isGeneratingBytes = false;
+      }); 
+    }
+    
+    // Fall back to sync call when no buffered bytes are available
+    if (this.bytesBufferIndex == -1) {
+      return crypto.randomBytes(bytes);
+    }
+  }
+  
+  var result = this.bytesBuffer.slice(bytes*this.bytesBufferIndex, bytes*(this.bytesBufferIndex+1)); 
+  this.bytesBufferIndex++; 
+  
+  return result;
+}
+
+/**
+ * Generates a base64 id
+ *
+ * (Original version from socket.io )
+ */
+
+Base64Id.prototype.generateId = function () {
+  var rand = new Buffer(15); // multiple of 3 for base64
+  if (!rand.writeInt32BE) {
+    return Math.abs(Math.random() * Math.random() * Date.now() | 0).toString()
+      + Math.abs(Math.random() * Math.random() * Date.now() | 0).toString();
+  }
+  this.sequenceNumber = (this.sequenceNumber + 1) | 0;
+  rand.writeInt32BE(this.sequenceNumber, 11);
+  if (crypto.randomBytes) {
+    this.getRandomBytes(12).copy(rand);
+  } else {
+    // not secure for node 0.4
+    [0, 4, 8].forEach(function(i) {
+      rand.writeInt32BE(Math.random() * Math.pow(2, 32) | 0, i);
+    });
+  }
+  return rand.toString('base64').replace(/\//g, '_').replace(/\+/g, '-');
+};
+
+/**
+ * Export
+ */
+
+exports = module.exports = new Base64Id();
diff --git a/cep/node_modules/base64id/package.json b/cep/node_modules/base64id/package.json
new file mode 100644
index 0000000..ecd8969
--- /dev/null
+++ b/cep/node_modules/base64id/package.json
@@ -0,0 +1,81 @@
+{
+  "_args": [
+    [
+      {
+        "raw": "base64id@0.1.0",
+        "scope": null,
+        "escapedName": "base64id",
+        "name": "base64id",
+        "rawSpec": "0.1.0",
+        "spec": "0.1.0",
+        "type": "version"
+      },
+      "D:\\repositories\\tk-framework-adobe\\extensions\\http_server\\node_modules\\engine.io"
+    ]
+  ],
+  "_defaultsLoaded": true,
+  "_engineSupported": true,
+  "_from": "base64id@0.1.0",
+  "_id": "base64id@0.1.0",
+  "_inCache": true,
+  "_installable": true,
+  "_location": "/base64id",
+  "_nodeVersion": "v0.6.15",
+  "_npmUser": {
+    "name": "faeldt_kristian",
+    "email": "faeldt_kristian@cyberagent.co.jp"
+  },
+  "_npmVersion": "1.1.16",
+  "_phantomChildren": {},
+  "_requested": {
+    "raw": "base64id@0.1.0",
+    "scope": null,
+    "escapedName": "base64id",
+    "name": "base64id",
+    "rawSpec": "0.1.0",
+    "spec": "0.1.0",
+    "type": "version"
+  },
+  "_requiredBy": [
+    "/engine.io"
+  ],
+  "_resolved": "https://registry.npmjs.org/base64id/-/base64id-0.1.0.tgz",
+  "_shasum": "02ce0fdeee0cef4f40080e1e73e834f0b1bfce3f",
+  "_shrinkwrap": null,
+  "_spec": "base64id@0.1.0",
+  "_where": "D:\\repositories\\tk-framework-adobe\\extensions\\http_server\\node_modules\\engine.io",
+  "author": {
+    "name": "Kristian Faeldt",
+    "email": "faeldt_kristian@cyberagent.co.jp"
+  },
+  "bugs": {
+    "url": "https://github.com/faeldt/base64id/issues"
+  },
+  "dependencies": {},
+  "description": "Generates a base64 id",
+  "devDependencies": {},
+  "directories": {},
+  "dist": {
+    "shasum": "02ce0fdeee0cef4f40080e1e73e834f0b1bfce3f",
+    "tarball": "https://registry.npmjs.org/base64id/-/base64id-0.1.0.tgz"
+  },
+  "engines": {
+    "node": ">= 0.4.0"
+  },
+  "homepage": "https://github.com/faeldt/base64id#readme",
+  "main": "./lib/base64id.js",
+  "maintainers": [
+    {
+      "name": "faeldt_kristian",
+      "email": "faeldt_kristian@cyberagent.co.jp"
+    }
+  ],
+  "name": "base64id",
+  "optionalDependencies": {},
+  "readme": "ERROR: No README data found!",
+  "repository": {
+    "type": "git",
+    "url": "git://github.com/faeldt/base64id.git"
+  },
+  "version": "0.1.0"
+}
diff --git a/cep/node_modules/benchmark/LICENSE.txt b/cep/node_modules/benchmark/LICENSE.txt
new file mode 100644
index 0000000..e33e63f
--- /dev/null
+++ b/cep/node_modules/benchmark/LICENSE.txt
@@ -0,0 +1,22 @@
+Copyright 2010-2012 Mathias Bynens 
+Based on JSLitmus.js, copyright Robert Kieffer 
+Modified by John-David Dalton 
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/cep/node_modules/benchmark/README.md b/cep/node_modules/benchmark/README.md
new file mode 100644
index 0000000..a3ab3f8
--- /dev/null
+++ b/cep/node_modules/benchmark/README.md
@@ -0,0 +1,131 @@
+# Benchmark.js v1.0.0
+
+A [robust](http://calendar.perfplanet.com/2010/bulletproof-javascript-benchmarks/ "Bulletproof JavaScript benchmarks") benchmarking library that works on nearly all JavaScript platforms1, supports high-resolution timers, and returns statistically significant results. As seen on [jsPerf](http://jsperf.com/).
+
+## BestieJS
+
+Benchmark.js is part of the BestieJS *"Best in Class"* module collection. This means we promote solid browser/environment support, ES5 precedents, unit testing, and plenty of documentation.
+
+## Documentation
+
+The documentation for Benchmark.js can be viewed here: 
+
+For a list of upcoming features, check out our [roadmap](https://github.com/bestiejs/benchmark.js/wiki/Roadmap).
+
+## Support
+
+Benchmark.js has been tested in at least Adobe AIR 3.1, Chrome 5-21, Firefox 1.5-13, IE 6-9, Opera 9.25-12.01, Safari 3-6, Node.js 0.8.6, Narwhal 0.3.2, RingoJS 0.8, and Rhino 1.7RC5.
+
+## Installation and usage
+
+In a browser or Adobe AIR:
+
+~~~ html
+
+~~~
+
+Optionally, expose Java’s nanosecond timer by adding the `nano` applet to the ``:
+
+~~~ html
+
+~~~
+
+Or enable Chrome’s microsecond timer by using the [command line switch](http://peter.sh/experiments/chromium-command-line-switches/#enable-benchmarking):
+
+    --enable-benchmarking
+
+Via [npm](http://npmjs.org/):
+
+~~~ bash
+npm install benchmark
+~~~
+
+In [Node.js](http://nodejs.org/) and [RingoJS v0.8.0+](http://ringojs.org/):
+
+~~~ js
+var Benchmark = require('benchmark');
+~~~
+
+Optionally, use the [microtime module](https://github.com/wadey/node-microtime) by Wade Simmons:
+
+~~~ bash
+npm install microtime
+~~~
+
+In [RingoJS v0.7.0-](http://ringojs.org/):
+
+~~~ js
+var Benchmark = require('benchmark').Benchmark;
+~~~
+
+In [Rhino](http://www.mozilla.org/rhino/):
+
+~~~ js
+load('benchmark.js');
+~~~
+
+In an AMD loader like [RequireJS](http://requirejs.org/):
+
+~~~ js
+require({
+  'paths': {
+    'benchmark': 'path/to/benchmark'
+  }
+},
+['benchmark'], function(Benchmark) {
+  console.log(Benchmark.version);
+});
+
+// or with platform.js
+// https://github.com/bestiejs/platform.js
+require({
+  'paths': {
+    'benchmark': 'path/to/benchmark',
+    'platform': 'path/to/platform'
+  }
+},
+['benchmark', 'platform'], function(Benchmark, platform) {
+  Benchmark.platform = platform;
+  console.log(Benchmark.platform.name);
+});
+~~~
+
+Usage example:
+
+~~~ js
+var suite = new Benchmark.Suite;
+
+// add tests
+suite.add('RegExp#test', function() {
+  /o/.test('Hello World!');
+})
+.add('String#indexOf', function() {
+  'Hello World!'.indexOf('o') > -1;
+})
+// add listeners
+.on('cycle', function(event) {
+  console.log(String(event.target));
+})
+.on('complete', function() {
+  console.log('Fastest is ' + this.filter('fastest').pluck('name'));
+})
+// run async
+.run({ 'async': true });
+
+// logs:
+// > RegExp#test x 4,161,532 +-0.99% (59 cycles)
+// > String#indexOf x 6,139,623 +-1.00% (131 cycles)
+// > Fastest is String#indexOf
+~~~
+
+## Authors
+
+* [Mathias Bynens](http://mathiasbynens.be/)
+  [![twitter/mathias](http://gravatar.com/avatar/24e08a9ea84deb17ae121074d0f17125?s=70)](https://twitter.com/mathias "Follow @mathias on Twitter")
+* [John-David Dalton](http://allyoucanleet.com/)
+  [![twitter/jdalton](http://gravatar.com/avatar/299a3d891ff1920b69c364d061007043?s=70)](https://twitter.com/jdalton "Follow @jdalton on Twitter")
+
+## Contributors
+
+* [Kit Cambridge](http://kitcambridge.github.com/)
+  [![twitter/kitcambridge](http://gravatar.com/avatar/6662a1d02f351b5ef2f8b4d815804661?s=70)](https://twitter.com/kitcambridge "Follow @kitcambridge on Twitter")
diff --git a/cep/node_modules/benchmark/benchmark.js b/cep/node_modules/benchmark/benchmark.js
new file mode 100644
index 0000000..d8eb309
--- /dev/null
+++ b/cep/node_modules/benchmark/benchmark.js
@@ -0,0 +1,3918 @@
+/*!
+ * Benchmark.js v1.0.0 
+ * Copyright 2010-2012 Mathias Bynens 
+ * Based on JSLitmus.js, copyright Robert Kieffer 
+ * Modified by John-David Dalton 
+ * Available under MIT license 
+ */
+;(function(window, undefined) {
+  'use strict';
+
+  /** Used to assign each benchmark an incrimented id */
+  var counter = 0;
+
+  /** Detect DOM document object */
+  var doc = isHostType(window, 'document') && document;
+
+  /** Detect free variable `define` */
+  var freeDefine = typeof define == 'function' &&
+    typeof define.amd == 'object' && define.amd && define;
+
+  /** Detect free variable `exports` */
+  var freeExports = typeof exports == 'object' && exports &&
+    (typeof global == 'object' && global && global == global.global && (window = global), exports);
+
+  /** Detect free variable `require` */
+  var freeRequire = typeof require == 'function' && require;
+
+  /** Used to crawl all properties regardless of enumerability */
+  var getAllKeys = Object.getOwnPropertyNames;
+
+  /** Used to get property descriptors */
+  var getDescriptor = Object.getOwnPropertyDescriptor;
+
+  /** Used in case an object doesn't have its own method */
+  var hasOwnProperty = {}.hasOwnProperty;
+
+  /** Used to check if an object is extensible */
+  var isExtensible = Object.isExtensible || function() { return true; };
+
+  /** Used to access Wade Simmons' Node microtime module */
+  var microtimeObject = req('microtime');
+
+  /** Used to access the browser's high resolution timer */
+  var perfObject = isHostType(window, 'performance') && performance;
+
+  /** Used to call the browser's high resolution timer */
+  var perfName = perfObject && (
+    perfObject.now && 'now' ||
+    perfObject.webkitNow && 'webkitNow'
+  );
+
+  /** Used to access Node's high resolution timer */
+  var processObject = isHostType(window, 'process') && process;
+
+  /** Used to check if an own property is enumerable */
+  var propertyIsEnumerable = {}.propertyIsEnumerable;
+
+  /** Used to set property descriptors */
+  var setDescriptor = Object.defineProperty;
+
+  /** Used to resolve a value's internal [[Class]] */
+  var toString = {}.toString;
+
+  /** Used to prevent a `removeChild` memory leak in IE < 9 */
+  var trash = doc && doc.createElement('div');
+
+  /** Used to integrity check compiled tests */
+  var uid = 'uid' + (+new Date);
+
+  /** Used to avoid infinite recursion when methods call each other */
+  var calledBy = {};
+
+  /** Used to avoid hz of Infinity */
+  var divisors = {
+    '1': 4096,
+    '2': 512,
+    '3': 64,
+    '4': 8,
+    '5': 0
+  };
+
+  /**
+   * T-Distribution two-tailed critical values for 95% confidence
+   * http://www.itl.nist.gov/div898/handbook/eda/section3/eda3672.htm
+   */
+  var tTable = {
+    '1':  12.706,'2':  4.303, '3':  3.182, '4':  2.776, '5':  2.571, '6':  2.447,
+    '7':  2.365, '8':  2.306, '9':  2.262, '10': 2.228, '11': 2.201, '12': 2.179,
+    '13': 2.16,  '14': 2.145, '15': 2.131, '16': 2.12,  '17': 2.11,  '18': 2.101,
+    '19': 2.093, '20': 2.086, '21': 2.08,  '22': 2.074, '23': 2.069, '24': 2.064,
+    '25': 2.06,  '26': 2.056, '27': 2.052, '28': 2.048, '29': 2.045, '30': 2.042,
+    'infinity': 1.96
+  };
+
+  /**
+   * Critical Mann-Whitney U-values for 95% confidence
+   * http://www.saburchill.com/IBbiology/stats/003.html
+   */
+  var uTable = {
+    '5':  [0, 1, 2],
+    '6':  [1, 2, 3, 5],
+    '7':  [1, 3, 5, 6, 8],
+    '8':  [2, 4, 6, 8, 10, 13],
+    '9':  [2, 4, 7, 10, 12, 15, 17],
+    '10': [3, 5, 8, 11, 14, 17, 20, 23],
+    '11': [3, 6, 9, 13, 16, 19, 23, 26, 30],
+    '12': [4, 7, 11, 14, 18, 22, 26, 29, 33, 37],
+    '13': [4, 8, 12, 16, 20, 24, 28, 33, 37, 41, 45],
+    '14': [5, 9, 13, 17, 22, 26, 31, 36, 40, 45, 50, 55],
+    '15': [5, 10, 14, 19, 24, 29, 34, 39, 44, 49, 54, 59, 64],
+    '16': [6, 11, 15, 21, 26, 31, 37, 42, 47, 53, 59, 64, 70, 75],
+    '17': [6, 11, 17, 22, 28, 34, 39, 45, 51, 57, 63, 67, 75, 81, 87],
+    '18': [7, 12, 18, 24, 30, 36, 42, 48, 55, 61, 67, 74, 80, 86, 93, 99],
+    '19': [7, 13, 19, 25, 32, 38, 45, 52, 58, 65, 72, 78, 85, 92, 99, 106, 113],
+    '20': [8, 14, 20, 27, 34, 41, 48, 55, 62, 69, 76, 83, 90, 98, 105, 112, 119, 127],
+    '21': [8, 15, 22, 29, 36, 43, 50, 58, 65, 73, 80, 88, 96, 103, 111, 119, 126, 134, 142],
+    '22': [9, 16, 23, 30, 38, 45, 53, 61, 69, 77, 85, 93, 101, 109, 117, 125, 133, 141, 150, 158],
+    '23': [9, 17, 24, 32, 40, 48, 56, 64, 73, 81, 89, 98, 106, 115, 123, 132, 140, 149, 157, 166, 175],
+    '24': [10, 17, 25, 33, 42, 50, 59, 67, 76, 85, 94, 102, 111, 120, 129, 138, 147, 156, 165, 174, 183, 192],
+    '25': [10, 18, 27, 35, 44, 53, 62, 71, 80, 89, 98, 107, 117, 126, 135, 145, 154, 163, 173, 182, 192, 201, 211],
+    '26': [11, 19, 28, 37, 46, 55, 64, 74, 83, 93, 102, 112, 122, 132, 141, 151, 161, 171, 181, 191, 200, 210, 220, 230],
+    '27': [11, 20, 29, 38, 48, 57, 67, 77, 87, 97, 107, 118, 125, 138, 147, 158, 168, 178, 188, 199, 209, 219, 230, 240, 250],
+    '28': [12, 21, 30, 40, 50, 60, 70, 80, 90, 101, 111, 122, 132, 143, 154, 164, 175, 186, 196, 207, 218, 228, 239, 250, 261, 272],
+    '29': [13, 22, 32, 42, 52, 62, 73, 83, 94, 105, 116, 127, 138, 149, 160, 171, 182, 193, 204, 215, 226, 238, 249, 260, 271, 282, 294],
+    '30': [13, 23, 33, 43, 54, 65, 76, 87, 98, 109, 120, 131, 143, 154, 166, 177, 189, 200, 212, 223, 235, 247, 258, 270, 282, 293, 305, 317]
+  };
+
+  /**
+   * An object used to flag environments/features.
+   *
+   * @static
+   * @memberOf Benchmark
+   * @type Object
+   */
+  var support = {};
+
+  (function() {
+
+    /**
+     * Detect Adobe AIR.
+     *
+     * @memberOf Benchmark.support
+     * @type Boolean
+     */
+    support.air = isClassOf(window.runtime, 'ScriptBridgingProxyObject');
+
+    /**
+     * Detect if `arguments` objects have the correct internal [[Class]] value.
+     *
+     * @memberOf Benchmark.support
+     * @type Boolean
+     */
+    support.argumentsClass = isClassOf(arguments, 'Arguments');
+
+    /**
+     * Detect if in a browser environment.
+     *
+     * @memberOf Benchmark.support
+     * @type Boolean
+     */
+    support.browser = doc && isHostType(window, 'navigator');
+
+    /**
+     * Detect if strings support accessing characters by index.
+     *
+     * @memberOf Benchmark.support
+     * @type Boolean
+     */
+    support.charByIndex =
+      // IE 8 supports indexes on string literals but not string objects
+      ('x'[0] + Object('x')[0]) == 'xx';
+
+    /**
+     * Detect if strings have indexes as own properties.
+     *
+     * @memberOf Benchmark.support
+     * @type Boolean
+     */
+    support.charByOwnIndex =
+      // Narwhal, Rhino, RingoJS, IE 8, and Opera < 10.52 support indexes on
+      // strings but don't detect them as own properties
+      support.charByIndex && hasKey('x', '0');
+
+    /**
+     * Detect if Java is enabled/exposed.
+     *
+     * @memberOf Benchmark.support
+     * @type Boolean
+     */
+    support.java = isClassOf(window.java, 'JavaPackage');
+
+    /**
+     * Detect if the Timers API exists.
+     *
+     * @memberOf Benchmark.support
+     * @type Boolean
+     */
+    support.timeout = isHostType(window, 'setTimeout') && isHostType(window, 'clearTimeout');
+
+    /**
+     * Detect if functions support decompilation.
+     *
+     * @name decompilation
+     * @memberOf Benchmark.support
+     * @type Boolean
+     */
+    try {
+      // Safari 2.x removes commas in object literals
+      // from Function#toString results
+      // http://webk.it/11609
+      // Firefox 3.6 and Opera 9.25 strip grouping
+      // parentheses from Function#toString results
+      // http://bugzil.la/559438
+      support.decompilation = Function(
+        'return (' + (function(x) { return { 'x': '' + (1 + x) + '', 'y': 0 }; }) + ')'
+      )()(0).x === '1';
+    } catch(e) {
+      support.decompilation = false;
+    }
+
+    /**
+     * Detect ES5+ property descriptor API.
+     *
+     * @name descriptors
+     * @memberOf Benchmark.support
+     * @type Boolean
+     */
+    try {
+      var o = {};
+      support.descriptors = (setDescriptor(o, o, o), 'value' in getDescriptor(o, o));
+    } catch(e) {
+      support.descriptors = false;
+    }
+
+    /**
+     * Detect ES5+ Object.getOwnPropertyNames().
+     *
+     * @name getAllKeys
+     * @memberOf Benchmark.support
+     * @type Boolean
+     */
+    try {
+      support.getAllKeys = /\bvalueOf\b/.test(getAllKeys(Object.prototype));
+    } catch(e) {
+      support.getAllKeys = false;
+    }
+
+    /**
+     * Detect if own properties are iterated before inherited properties (all but IE < 9).
+     *
+     * @name iteratesOwnLast
+     * @memberOf Benchmark.support
+     * @type Boolean
+     */
+    support.iteratesOwnFirst = (function() {
+      var props = [];
+      function ctor() { this.x = 1; }
+      ctor.prototype = { 'y': 1 };
+      for (var prop in new ctor) { props.push(prop); }
+      return props[0] == 'x';
+    }());
+
+    /**
+     * Detect if a node's [[Class]] is resolvable (all but IE < 9)
+     * and that the JS engine errors when attempting to coerce an object to a
+     * string without a `toString` property value of `typeof` "function".
+     *
+     * @name nodeClass
+     * @memberOf Benchmark.support
+     * @type Boolean
+     */
+    try {
+      support.nodeClass = ({ 'toString': 0 } + '', toString.call(doc || 0) != '[object Object]');
+    } catch(e) {
+      support.nodeClass = true;
+    }
+  }());
+
+  /**
+   * Timer object used by `clock()` and `Deferred#resolve`.
+   *
+   * @private
+   * @type Object
+   */
+  var timer = {
+
+   /**
+    * The timer namespace object or constructor.
+    *
+    * @private
+    * @memberOf timer
+    * @type Function|Object
+    */
+    'ns': Date,
+
+   /**
+    * Starts the deferred timer.
+    *
+    * @private
+    * @memberOf timer
+    * @param {Object} deferred The deferred instance.
+    */
+    'start': null, // lazy defined in `clock()`
+
+   /**
+    * Stops the deferred timer.
+    *
+    * @private
+    * @memberOf timer
+    * @param {Object} deferred The deferred instance.
+    */
+    'stop': null // lazy defined in `clock()`
+  };
+
+  /** Shortcut for inverse results */
+  var noArgumentsClass = !support.argumentsClass,
+      noCharByIndex = !support.charByIndex,
+      noCharByOwnIndex = !support.charByOwnIndex;
+
+  /** Math shortcuts */
+  var abs   = Math.abs,
+      floor = Math.floor,
+      max   = Math.max,
+      min   = Math.min,
+      pow   = Math.pow,
+      sqrt  = Math.sqrt;
+
+  /*--------------------------------------------------------------------------*/
+
+  /**
+   * The Benchmark constructor.
+   *
+   * @constructor
+   * @param {String} name A name to identify the benchmark.
+   * @param {Function|String} fn The test to benchmark.
+   * @param {Object} [options={}] Options object.
+   * @example
+   *
+   * // basic usage (the `new` operator is optional)
+   * var bench = new Benchmark(fn);
+   *
+   * // or using a name first
+   * var bench = new Benchmark('foo', fn);
+   *
+   * // or with options
+   * var bench = new Benchmark('foo', fn, {
+   *
+   *   // displayed by Benchmark#toString if `name` is not available
+   *   'id': 'xyz',
+   *
+   *   // called when the benchmark starts running
+   *   'onStart': onStart,
+   *
+   *   // called after each run cycle
+   *   'onCycle': onCycle,
+   *
+   *   // called when aborted
+   *   'onAbort': onAbort,
+   *
+   *   // called when a test errors
+   *   'onError': onError,
+   *
+   *   // called when reset
+   *   'onReset': onReset,
+   *
+   *   // called when the benchmark completes running
+   *   'onComplete': onComplete,
+   *
+   *   // compiled/called before the test loop
+   *   'setup': setup,
+   *
+   *   // compiled/called after the test loop
+   *   'teardown': teardown
+   * });
+   *
+   * // or name and options
+   * var bench = new Benchmark('foo', {
+   *
+   *   // a flag to indicate the benchmark is deferred
+   *   'defer': true,
+   *
+   *   // benchmark test function
+   *   'fn': function(deferred) {
+   *     // call resolve() when the deferred test is finished
+   *     deferred.resolve();
+   *   }
+   * });
+   *
+   * // or options only
+   * var bench = new Benchmark({
+   *
+   *   // benchmark name
+   *   'name': 'foo',
+   *
+   *   // benchmark test as a string
+   *   'fn': '[1,2,3,4].sort()'
+   * });
+   *
+   * // a test's `this` binding is set to the benchmark instance
+   * var bench = new Benchmark('foo', function() {
+   *   'My name is '.concat(this.name); // My name is foo
+   * });
+   */
+  function Benchmark(name, fn, options) {
+    var me = this;
+
+    // allow instance creation without the `new` operator
+    if (me == null || me.constructor != Benchmark) {
+      return new Benchmark(name, fn, options);
+    }
+    // juggle arguments
+    if (isClassOf(name, 'Object')) {
+      // 1 argument (options)
+      options = name;
+    }
+    else if (isClassOf(name, 'Function')) {
+      // 2 arguments (fn, options)
+      options = fn;
+      fn = name;
+    }
+    else if (isClassOf(fn, 'Object')) {
+      // 2 arguments (name, options)
+      options = fn;
+      fn = null;
+      me.name = name;
+    }
+    else {
+      // 3 arguments (name, fn [, options])
+      me.name = name;
+    }
+    setOptions(me, options);
+    me.id || (me.id = ++counter);
+    me.fn == null && (me.fn = fn);
+    me.stats = deepClone(me.stats);
+    me.times = deepClone(me.times);
+  }
+
+  /**
+   * The Deferred constructor.
+   *
+   * @constructor
+   * @memberOf Benchmark
+   * @param {Object} clone The cloned benchmark instance.
+   */
+  function Deferred(clone) {
+    var me = this;
+    if (me == null || me.constructor != Deferred) {
+      return new Deferred(clone);
+    }
+    me.benchmark = clone;
+    clock(me);
+  }
+
+  /**
+   * The Event constructor.
+   *
+   * @constructor
+   * @memberOf Benchmark
+   * @param {String|Object} type The event type.
+   */
+  function Event(type) {
+    var me = this;
+    return (me == null || me.constructor != Event)
+      ? new Event(type)
+      : (type instanceof Event)
+          ? type
+          : extend(me, { 'timeStamp': +new Date }, typeof type == 'string' ? { 'type': type } : type);
+  }
+
+  /**
+   * The Suite constructor.
+   *
+   * @constructor
+   * @memberOf Benchmark
+   * @param {String} name A name to identify the suite.
+   * @param {Object} [options={}] Options object.
+   * @example
+   *
+   * // basic usage (the `new` operator is optional)
+   * var suite = new Benchmark.Suite;
+   *
+   * // or using a name first
+   * var suite = new Benchmark.Suite('foo');
+   *
+   * // or with options
+   * var suite = new Benchmark.Suite('foo', {
+   *
+   *   // called when the suite starts running
+   *   'onStart': onStart,
+   *
+   *   // called between running benchmarks
+   *   'onCycle': onCycle,
+   *
+   *   // called when aborted
+   *   'onAbort': onAbort,
+   *
+   *   // called when a test errors
+   *   'onError': onError,
+   *
+   *   // called when reset
+   *   'onReset': onReset,
+   *
+   *   // called when the suite completes running
+   *   'onComplete': onComplete
+   * });
+   */
+  function Suite(name, options) {
+    var me = this;
+
+    // allow instance creation without the `new` operator
+    if (me == null || me.constructor != Suite) {
+      return new Suite(name, options);
+    }
+    // juggle arguments
+    if (isClassOf(name, 'Object')) {
+      // 1 argument (options)
+      options = name;
+    } else {
+      // 2 arguments (name [, options])
+      me.name = name;
+    }
+    setOptions(me, options);
+  }
+
+  /*--------------------------------------------------------------------------*/
+
+  /**
+   * Note: Some array methods have been implemented in plain JavaScript to avoid
+   * bugs in IE, Opera, Rhino, and Mobile Safari.
+   *
+   * IE compatibility mode and IE < 9 have buggy Array `shift()` and `splice()`
+   * functions that fail to remove the last element, `object[0]`, of
+   * array-like-objects even though the `length` property is set to `0`.
+   * The `shift()` method is buggy in IE 8 compatibility mode, while `splice()`
+   * is buggy regardless of mode in IE < 9 and buggy in compatibility mode in IE 9.
+   *
+   * In Opera < 9.50 and some older/beta Mobile Safari versions using `unshift()`
+   * generically to augment the `arguments` object will pave the value at index 0
+   * without incrimenting the other values's indexes.
+   * https://github.com/documentcloud/underscore/issues/9
+   *
+   * Rhino and environments it powers, like Narwhal and RingoJS, may have
+   * buggy Array `concat()`, `reverse()`, `shift()`, `slice()`, `splice()` and
+   * `unshift()` functions that make sparse arrays non-sparse by assigning the
+   * undefined indexes a value of undefined.
+   * https://github.com/mozilla/rhino/commit/702abfed3f8ca043b2636efd31c14ba7552603dd
+   */
+
+  /**
+   * Creates an array containing the elements of the host array followed by the
+   * elements of each argument in order.
+   *
+   * @memberOf Benchmark.Suite
+   * @returns {Array} The new array.
+   */
+  function concat() {
+    var value,
+        j = -1,
+        length = arguments.length,
+        result = slice.call(this),
+        index = result.length;
+
+    while (++j < length) {
+      value = arguments[j];
+      if (isClassOf(value, 'Array')) {
+        for (var k = 0, l = value.length; k < l; k++, index++) {
+          if (k in value) {
+            result[index] = value[k];
+          }
+        }
+      } else {
+        result[index++] = value;
+      }
+    }
+    return result;
+  }
+
+  /**
+   * Utility function used by `shift()`, `splice()`, and `unshift()`.
+   *
+   * @private
+   * @param {Number} start The index to start inserting elements.
+   * @param {Number} deleteCount The number of elements to delete from the insert point.
+   * @param {Array} elements The elements to insert.
+   * @returns {Array} An array of deleted elements.
+   */
+  function insert(start, deleteCount, elements) {
+    // `result` should have its length set to the `deleteCount`
+    // see https://bugs.ecmascript.org/show_bug.cgi?id=332
+    var deleteEnd = start + deleteCount,
+        elementCount = elements ? elements.length : 0,
+        index = start - 1,
+        length = start + elementCount,
+        object = this,
+        result = Array(deleteCount),
+        tail = slice.call(object, deleteEnd);
+
+    // delete elements from the array
+    while (++index < deleteEnd) {
+      if (index in object) {
+        result[index - start] = object[index];
+        delete object[index];
+      }
+    }
+    // insert elements
+    index = start - 1;
+    while (++index < length) {
+      object[index] = elements[index - start];
+    }
+    // append tail elements
+    start = index--;
+    length = max(0, (object.length >>> 0) - deleteCount + elementCount);
+    while (++index < length) {
+      if ((index - start) in tail) {
+        object[index] = tail[index - start];
+      } else if (index in object) {
+        delete object[index];
+      }
+    }
+    // delete excess elements
+    deleteCount = deleteCount > elementCount ? deleteCount - elementCount : 0;
+    while (deleteCount--) {
+      index = length + deleteCount;
+      if (index in object) {
+        delete object[index];
+      }
+    }
+    object.length = length;
+    return result;
+  }
+
+  /**
+   * Rearrange the host array's elements in reverse order.
+   *
+   * @memberOf Benchmark.Suite
+   * @returns {Array} The reversed array.
+   */
+  function reverse() {
+    var upperIndex,
+        value,
+        index = -1,
+        object = Object(this),
+        length = object.length >>> 0,
+        middle = floor(length / 2);
+
+    if (length > 1) {
+      while (++index < middle) {
+        upperIndex = length - index - 1;
+        value = upperIndex in object ? object[upperIndex] : uid;
+        if (index in object) {
+          object[upperIndex] = object[index];
+        } else {
+          delete object[upperIndex];
+        }
+        if (value != uid) {
+          object[index] = value;
+        } else {
+          delete object[index];
+        }
+      }
+    }
+    return object;
+  }
+
+  /**
+   * Removes the first element of the host array and returns it.
+   *
+   * @memberOf Benchmark.Suite
+   * @returns {Mixed} The first element of the array.
+   */
+  function shift() {
+    return insert.call(this, 0, 1)[0];
+  }
+
+  /**
+   * Creates an array of the host array's elements from the start index up to,
+   * but not including, the end index.
+   *
+   * @memberOf Benchmark.Suite
+   * @param {Number} start The starting index.
+   * @param {Number} end The end index.
+   * @returns {Array} The new array.
+   */
+  function slice(start, end) {
+    var index = -1,
+        object = Object(this),
+        length = object.length >>> 0,
+        result = [];
+
+    start = toInteger(start);
+    start = start < 0 ? max(length + start, 0) : min(start, length);
+    start--;
+    end = end == null ? length : toInteger(end);
+    end = end < 0 ? max(length + end, 0) : min(end, length);
+
+    while ((++index, ++start) < end) {
+      if (start in object) {
+        result[index] = object[start];
+      }
+    }
+    return result;
+  }
+
+  /**
+   * Allows removing a range of elements and/or inserting elements into the
+   * host array.
+   *
+   * @memberOf Benchmark.Suite
+   * @param {Number} start The start index.
+   * @param {Number} deleteCount The number of elements to delete.
+   * @param {Mixed} [val1, val2, ...] values to insert at the `start` index.
+   * @returns {Array} An array of removed elements.
+   */
+  function splice(start, deleteCount) {
+    var object = Object(this),
+        length = object.length >>> 0;
+
+    start = toInteger(start);
+    start = start < 0 ? max(length + start, 0) : min(start, length);
+
+    // support the de-facto SpiderMonkey extension
+    // https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/splice#Parameters
+    // https://bugs.ecmascript.org/show_bug.cgi?id=429
+    deleteCount = arguments.length == 1
+      ? length - start
+      : min(max(toInteger(deleteCount), 0), length - start);
+
+    return insert.call(object, start, deleteCount, slice.call(arguments, 2));
+  }
+
+  /**
+   * Converts the specified `value` to an integer.
+   *
+   * @private
+   * @param {Mixed} value The value to convert.
+   * @returns {Number} The resulting integer.
+   */
+  function toInteger(value) {
+    value = +value;
+    return value === 0 || !isFinite(value) ? value || 0 : value - (value % 1);
+  }
+
+  /**
+   * Appends arguments to the host array.
+   *
+   * @memberOf Benchmark.Suite
+   * @returns {Number} The new length.
+   */
+  function unshift() {
+    var object = Object(this);
+    insert.call(object, 0, 0, arguments);
+    return object.length;
+  }
+
+  /*--------------------------------------------------------------------------*/
+
+  /**
+   * A generic `Function#bind` like method.
+   *
+   * @private
+   * @param {Function} fn The function to be bound to `thisArg`.
+   * @param {Mixed} thisArg The `this` binding for the given function.
+   * @returns {Function} The bound function.
+   */
+  function bind(fn, thisArg) {
+    return function() { fn.apply(thisArg, arguments); };
+  }
+
+  /**
+   * Creates a function from the given arguments string and body.
+   *
+   * @private
+   * @param {String} args The comma separated function arguments.
+   * @param {String} body The function body.
+   * @returns {Function} The new function.
+   */
+  function createFunction() {
+    // lazy define
+    createFunction = function(args, body) {
+      var result,
+          anchor = freeDefine ? define.amd : Benchmark,
+          prop = uid + 'createFunction';
+
+      runScript((freeDefine ? 'define.amd.' : 'Benchmark.') + prop + '=function(' + args + '){' + body + '}');
+      result = anchor[prop];
+      delete anchor[prop];
+      return result;
+    };
+    // fix JaegerMonkey bug
+    // http://bugzil.la/639720
+    createFunction = support.browser && (createFunction('', 'return"' + uid + '"') || noop)() == uid ? createFunction : Function;
+    return createFunction.apply(null, arguments);
+  }
+
+  /**
+   * Delay the execution of a function based on the benchmark's `delay` property.
+   *
+   * @private
+   * @param {Object} bench The benchmark instance.
+   * @param {Object} fn The function to execute.
+   */
+  function delay(bench, fn) {
+    bench._timerId = setTimeout(fn, bench.delay * 1e3);
+  }
+
+  /**
+   * Destroys the given element.
+   *
+   * @private
+   * @param {Element} element The element to destroy.
+   */
+  function destroyElement(element) {
+    trash.appendChild(element);
+    trash.innerHTML = '';
+  }
+
+  /**
+   * Iterates over an object's properties, executing the `callback` for each.
+   * Callbacks may terminate the loop by explicitly returning `false`.
+   *
+   * @private
+   * @param {Object} object The object to iterate over.
+   * @param {Function} callback The function executed per own property.
+   * @param {Object} options The options object.
+   * @returns {Object} Returns the object iterated over.
+   */
+  function forProps() {
+    var forShadowed,
+        skipSeen,
+        forArgs = true,
+        shadowed = ['constructor', 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable', 'toLocaleString', 'toString', 'valueOf'];
+
+    (function(enumFlag, key) {
+      // must use a non-native constructor to catch the Safari 2 issue
+      function Klass() { this.valueOf = 0; };
+      Klass.prototype.valueOf = 0;
+      // check various for-in bugs
+      for (key in new Klass) {
+        enumFlag += key == 'valueOf' ? 1 : 0;
+      }
+      // check if `arguments` objects have non-enumerable indexes
+      for (key in arguments) {
+        key == '0' && (forArgs = false);
+      }
+      // Safari 2 iterates over shadowed properties twice
+      // http://replay.waybackmachine.org/20090428222941/http://tobielangel.com/2007/1/29/for-in-loop-broken-in-safari/
+      skipSeen = enumFlag == 2;
+      // IE < 9 incorrectly makes an object's properties non-enumerable if they have
+      // the same name as other non-enumerable properties in its prototype chain.
+      forShadowed = !enumFlag;
+    }(0));
+
+    // lazy define
+    forProps = function(object, callback, options) {
+      options || (options = {});
+
+      var result = object;
+      object = Object(object);
+
+      var ctor,
+          key,
+          keys,
+          skipCtor,
+          done = !result,
+          which = options.which,
+          allFlag = which == 'all',
+          index = -1,
+          iteratee = object,
+          length = object.length,
+          ownFlag = allFlag || which == 'own',
+          seen = {},
+          skipProto = isClassOf(object, 'Function'),
+          thisArg = options.bind;
+
+      if (thisArg !== undefined) {
+        callback = bind(callback, thisArg);
+      }
+      // iterate all properties
+      if (allFlag && support.getAllKeys) {
+        for (index = 0, keys = getAllKeys(object), length = keys.length; index < length; index++) {
+          key = keys[index];
+          if (callback(object[key], key, object) === false) {
+            break;
+          }
+        }
+      }
+      // else iterate only enumerable properties
+      else {
+        for (key in object) {
+          // Firefox < 3.6, Opera > 9.50 - Opera < 11.60, and Safari < 5.1
+          // (if the prototype or a property on the prototype has been set)
+          // incorrectly set a function's `prototype` property [[Enumerable]] value
+          // to `true`. Because of this we standardize on skipping the `prototype`
+          // property of functions regardless of their [[Enumerable]] value.
+          if ((done =
+              !(skipProto && key == 'prototype') &&
+              !(skipSeen && (hasKey(seen, key) || !(seen[key] = true))) &&
+              (!ownFlag || ownFlag && hasKey(object, key)) &&
+              callback(object[key], key, object) === false)) {
+            break;
+          }
+        }
+        // in IE < 9 strings don't support accessing characters by index
+        if (!done && (forArgs && isArguments(object) ||
+            ((noCharByIndex || noCharByOwnIndex) && isClassOf(object, 'String') &&
+              (iteratee = noCharByIndex ? object.split('') : object)))) {
+          while (++index < length) {
+            if ((done =
+                callback(iteratee[index], String(index), object) === false)) {
+              break;
+            }
+          }
+        }
+        if (!done && forShadowed) {
+          // Because IE < 9 can't set the `[[Enumerable]]` attribute of an existing
+          // property and the `constructor` property of a prototype defaults to
+          // non-enumerable, we manually skip the `constructor` property when we
+          // think we are iterating over a `prototype` object.
+          ctor = object.constructor;
+          skipCtor = ctor && ctor.prototype && ctor.prototype.constructor === ctor;
+          for (index = 0; index < 7; index++) {
+            key = shadowed[index];
+            if (!(skipCtor && key == 'constructor') &&
+                hasKey(object, key) &&
+                callback(object[key], key, object) === false) {
+              break;
+            }
+          }
+        }
+      }
+      return result;
+    };
+    return forProps.apply(null, arguments);
+  }
+
+  /**
+   * Gets the name of the first argument from a function's source.
+   *
+   * @private
+   * @param {Function} fn The function.
+   * @returns {String} The argument name.
+   */
+  function getFirstArgument(fn) {
+    return (!hasKey(fn, 'toString') &&
+      (/^[\s(]*function[^(]*\(([^\s,)]+)/.exec(fn) || 0)[1]) || '';
+  }
+
+  /**
+   * Computes the arithmetic mean of a sample.
+   *
+   * @private
+   * @param {Array} sample The sample.
+   * @returns {Number} The mean.
+   */
+  function getMean(sample) {
+    return reduce(sample, function(sum, x) {
+      return sum + x;
+    }) / sample.length || 0;
+  }
+
+  /**
+   * Gets the source code of a function.
+   *
+   * @private
+   * @param {Function} fn The function.
+   * @param {String} altSource A string used when a function's source code is unretrievable.
+   * @returns {String} The function's source code.
+   */
+  function getSource(fn, altSource) {
+    var result = altSource;
+    if (isStringable(fn)) {
+      result = String(fn);
+    } else if (support.decompilation) {
+      // escape the `{` for Firefox 1
+      result = (/^[^{]+\{([\s\S]*)}\s*$/.exec(fn) || 0)[1];
+    }
+    // trim string
+    result = (result || '').replace(/^\s+|\s+$/g, '');
+
+    // detect strings containing only the "use strict" directive
+    return /^(?:\/\*+[\w|\W]*?\*\/|\/\/.*?[\n\r\u2028\u2029]|\s)*(["'])use strict\1;?$/.test(result)
+      ? ''
+      : result;
+  }
+
+  /**
+   * Checks if a value is an `arguments` object.
+   *
+   * @private
+   * @param {Mixed} value The value to check.
+   * @returns {Boolean} Returns `true` if the value is an `arguments` object, else `false`.
+   */
+  function isArguments() {
+    // lazy define
+    isArguments = function(value) {
+      return toString.call(value) == '[object Arguments]';
+    };
+    if (noArgumentsClass) {
+      isArguments = function(value) {
+        return hasKey(value, 'callee') &&
+          !(propertyIsEnumerable && propertyIsEnumerable.call(value, 'callee'));
+      };
+    }
+    return isArguments(arguments[0]);
+  }
+
+  /**
+   * Checks if an object is of the specified class.
+   *
+   * @private
+   * @param {Mixed} value The value to check.
+   * @param {String} name The name of the class.
+   * @returns {Boolean} Returns `true` if the value is of the specified class, else `false`.
+   */
+  function isClassOf(value, name) {
+    return value != null && toString.call(value) == '[object ' + name + ']';
+  }
+
+  /**
+   * Host objects can return type values that are different from their actual
+   * data type. The objects we are concerned with usually return non-primitive
+   * types of object, function, or unknown.
+   *
+   * @private
+   * @param {Mixed} object The owner of the property.
+   * @param {String} property The property to check.
+   * @returns {Boolean} Returns `true` if the property value is a non-primitive, else `false`.
+   */
+  function isHostType(object, property) {
+    var type = object != null ? typeof object[property] : 'number';
+    return !/^(?:boolean|number|string|undefined)$/.test(type) &&
+      (type == 'object' ? !!object[property] : true);
+  }
+
+  /**
+   * 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 {Mixed} value The value to check.
+   * @returns {Boolean} Returns `true` if the `value` is a plain `Object` object, else `false`.
+   */
+  function isPlainObject(value) {
+    // avoid non-objects and false positives for `arguments` objects in IE < 9
+    var result = false;
+    if (!(value && typeof value == 'object') || (noArgumentsClass && isArguments(value))) {
+      return result;
+    }
+    // IE < 9 presents DOM nodes as `Object` objects except they have `toString`
+    // methods that are `typeof` "string" and still can coerce nodes to strings.
+    // Also check that the constructor is `Object` (i.e. `Object instanceof Object`)
+    var ctor = value.constructor;
+    if ((support.nodeClass || !(typeof value.toString != 'function' && typeof (value + '') == 'string')) &&
+        (!isClassOf(ctor, 'Function') || ctor instanceof ctor)) {
+      // 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.
+      if (support.iteratesOwnFirst) {
+        forProps(value, function(subValue, subKey) {
+          result = subKey;
+        });
+        return result === false || hasKey(value, result);
+      }
+      // IE < 9 iterates inherited properties before own properties. If the first
+      // iterated property is an object's own property then there are no inherited
+      // enumerable properties.
+      forProps(value, function(subValue, subKey) {
+        result = !hasKey(value, subKey);
+        return false;
+      });
+      return result === false;
+    }
+    return result;
+  }
+
+  /**
+   * Checks if a value can be safely coerced to a string.
+   *
+   * @private
+   * @param {Mixed} value The value to check.
+   * @returns {Boolean} Returns `true` if the value can be coerced, else `false`.
+   */
+  function isStringable(value) {
+    return hasKey(value, 'toString') || isClassOf(value, 'String');
+  }
+
+  /**
+   * Wraps a function and passes `this` to the original function as the
+   * first argument.
+   *
+   * @private
+   * @param {Function} fn The function to be wrapped.
+   * @returns {Function} The new function.
+   */
+  function methodize(fn) {
+    return function() {
+      var args = [this];
+      args.push.apply(args, arguments);
+      return fn.apply(null, args);
+    };
+  }
+
+  /**
+   * A no-operation function.
+   *
+   * @private
+   */
+  function noop() {
+    // no operation performed
+  }
+
+  /**
+   * A wrapper around require() to suppress `module missing` errors.
+   *
+   * @private
+   * @param {String} id The module id.
+   * @returns {Mixed} The exported module or `null`.
+   */
+  function req(id) {
+    try {
+      var result = freeExports && freeRequire(id);
+    } catch(e) { }
+    return result || null;
+  }
+
+  /**
+   * Runs a snippet of JavaScript via script injection.
+   *
+   * @private
+   * @param {String} code The code to run.
+   */
+  function runScript(code) {
+    var anchor = freeDefine ? define.amd : Benchmark,
+        script = doc.createElement('script'),
+        sibling = doc.getElementsByTagName('script')[0],
+        parent = sibling.parentNode,
+        prop = uid + 'runScript',
+        prefix = '(' + (freeDefine ? 'define.amd.' : 'Benchmark.') + prop + '||function(){})();';
+
+    // Firefox 2.0.0.2 cannot use script injection as intended because it executes
+    // asynchronously, but that's OK because script injection is only used to avoid
+    // the previously commented JaegerMonkey bug.
+    try {
+      // remove the inserted script *before* running the code to avoid differences
+      // in the expected script element count/order of the document.
+      script.appendChild(doc.createTextNode(prefix + code));
+      anchor[prop] = function() { destroyElement(script); };
+    } catch(e) {
+      parent = parent.cloneNode(false);
+      sibling = null;
+      script.text = code;
+    }
+    parent.insertBefore(script, sibling);
+    delete anchor[prop];
+  }
+
+  /**
+   * A helper function for setting options/event handlers.
+   *
+   * @private
+   * @param {Object} bench The benchmark instance.
+   * @param {Object} [options={}] Options object.
+   */
+  function setOptions(bench, options) {
+    options = extend({}, bench.constructor.options, options);
+    bench.options = forOwn(options, function(value, key) {
+      if (value != null) {
+        // add event listeners
+        if (/^on[A-Z]/.test(key)) {
+          forEach(key.split(' '), function(key) {
+            bench.on(key.slice(2).toLowerCase(), value);
+          });
+        } else if (!hasKey(bench, key)) {
+          bench[key] = deepClone(value);
+        }
+      }
+    });
+  }
+
+  /*--------------------------------------------------------------------------*/
+
+  /**
+   * Handles cycling/completing the deferred benchmark.
+   *
+   * @memberOf Benchmark.Deferred
+   */
+  function resolve() {
+    var me = this,
+        clone = me.benchmark,
+        bench = clone._original;
+
+    if (bench.aborted) {
+      // cycle() -> clone cycle/complete event -> compute()'s invoked bench.run() cycle/complete
+      me.teardown();
+      clone.running = false;
+      cycle(me);
+    }
+    else if (++me.cycles < clone.count) {
+      // continue the test loop
+      if (support.timeout) {
+        // use setTimeout to avoid a call stack overflow if called recursively
+        setTimeout(function() { clone.compiled.call(me, timer); }, 0);
+      } else {
+        clone.compiled.call(me, timer);
+      }
+    }
+    else {
+      timer.stop(me);
+      me.teardown();
+      delay(clone, function() { cycle(me); });
+    }
+  }
+
+  /*--------------------------------------------------------------------------*/
+
+  /**
+   * A deep clone utility.
+   *
+   * @static
+   * @memberOf Benchmark
+   * @param {Mixed} value The value to clone.
+   * @returns {Mixed} The cloned value.
+   */
+  function deepClone(value) {
+    var accessor,
+        circular,
+        clone,
+        ctor,
+        descriptor,
+        extensible,
+        key,
+        length,
+        markerKey,
+        parent,
+        result,
+        source,
+        subIndex,
+        data = { 'value': value },
+        index = 0,
+        marked = [],
+        queue = { 'length': 0 },
+        unmarked = [];
+
+    /**
+     * An easily detectable decorator for cloned values.
+     */
+    function Marker(object) {
+      this.raw = object;
+    }
+
+    /**
+     * The callback used by `forProps()`.
+     */
+    function forPropsCallback(subValue, subKey) {
+      // exit early to avoid cloning the marker
+      if (subValue && subValue.constructor == Marker) {
+        return;
+      }
+      // add objects to the queue
+      if (subValue === Object(subValue)) {
+        queue[queue.length++] = { 'key': subKey, 'parent': clone, 'source': value };
+      }
+      // assign non-objects
+      else {
+        try {
+          // will throw an error in strict mode if the property is read-only
+          clone[subKey] = subValue;
+        } catch(e) { }
+      }
+    }
+
+    /**
+     * Gets an available marker key for the given object.
+     */
+    function getMarkerKey(object) {
+      // avoid collisions with existing keys
+      var result = uid;
+      while (object[result] && object[result].constructor != Marker) {
+        result += 1;
+      }
+      return result;
+    }
+
+    do {
+      key = data.key;
+      parent = data.parent;
+      source = data.source;
+      clone = value = source ? source[key] : data.value;
+      accessor = circular = descriptor = false;
+
+      // create a basic clone to filter out functions, DOM elements, and
+      // other non `Object` objects
+      if (value === Object(value)) {
+        // use custom deep clone function if available
+        if (isClassOf(value.deepClone, 'Function')) {
+          clone = value.deepClone();
+        } else {
+          ctor = value.constructor;
+          switch (toString.call(value)) {
+            case '[object Array]':
+              clone = new ctor(value.length);
+              break;
+
+            case '[object Boolean]':
+              clone = new ctor(value == true);
+              break;
+
+            case '[object Date]':
+              clone = new ctor(+value);
+              break;
+
+            case '[object Object]':
+              isPlainObject(value) && (clone = {});
+              break;
+
+            case '[object Number]':
+            case '[object String]':
+              clone = new ctor(value);
+              break;
+
+            case '[object RegExp]':
+              clone = ctor(value.source,
+                (value.global     ? 'g' : '') +
+                (value.ignoreCase ? 'i' : '') +
+                (value.multiline  ? 'm' : ''));
+          }
+        }
+        // continue clone if `value` doesn't have an accessor descriptor
+        // http://es5.github.com/#x8.10.1
+        if (clone && clone != value &&
+            !(descriptor = source && support.descriptors && getDescriptor(source, key),
+              accessor = descriptor && (descriptor.get || descriptor.set))) {
+          // use an existing clone (circular reference)
+          if ((extensible = isExtensible(value))) {
+            markerKey = getMarkerKey(value);
+            if (value[markerKey]) {
+              circular = clone = value[markerKey].raw;
+            }
+          } else {
+            // for frozen/sealed objects
+            for (subIndex = 0, length = unmarked.length; subIndex < length; subIndex++) {
+              data = unmarked[subIndex];
+              if (data.object === value) {
+                circular = clone = data.clone;
+                break;
+              }
+            }
+          }
+          if (!circular) {
+            // mark object to allow quickly detecting circular references and tie it to its clone
+            if (extensible) {
+              value[markerKey] = new Marker(clone);
+              marked.push({ 'key': markerKey, 'object': value });
+            } else {
+              // for frozen/sealed objects
+              unmarked.push({ 'clone': clone, 'object': value });
+            }
+            // iterate over object properties
+            forProps(value, forPropsCallback, { 'which': 'all' });
+          }
+        }
+      }
+      if (parent) {
+        // for custom property descriptors
+        if (accessor || (descriptor && !(descriptor.configurable && descriptor.enumerable && descriptor.writable))) {
+          if ('value' in descriptor) {
+            descriptor.value = clone;
+          }
+          setDescriptor(parent, key, descriptor);
+        }
+        // for default property descriptors
+        else {
+          parent[key] = clone;
+        }
+      } else {
+        result = clone;
+      }
+    } while ((data = queue[index++]));
+
+    // remove markers
+    for (index = 0, length = marked.length; index < length; index++) {
+      data = marked[index];
+      delete data.object[data.key];
+    }
+    return result;
+  }
+
+  /**
+   * An iteration utility for arrays and objects.
+   * Callbacks may terminate the loop by explicitly returning `false`.
+   *
+   * @static
+   * @memberOf Benchmark
+   * @param {Array|Object} object The object to iterate over.
+   * @param {Function} callback The function called per iteration.
+   * @param {Mixed} thisArg The `this` binding for the callback.
+   * @returns {Array|Object} Returns the object iterated over.
+   */
+  function each(object, callback, thisArg) {
+    var result = object;
+    object = Object(object);
+
+    var fn = callback,
+        index = -1,
+        length = object.length,
+        isSnapshot = !!(object.snapshotItem && (length = object.snapshotLength)),
+        isSplittable = (noCharByIndex || noCharByOwnIndex) && isClassOf(object, 'String'),
+        isConvertable = isSnapshot || isSplittable || 'item' in object,
+        origObject = object;
+
+    // in Opera < 10.5 `hasKey(object, 'length')` returns `false` for NodeLists
+    if (length === length >>> 0) {
+      if (isConvertable) {
+        // the third argument of the callback is the original non-array object
+        callback = function(value, index) {
+          return fn.call(this, value, index, origObject);
+        };
+        // in IE < 9 strings don't support accessing characters by index
+        if (isSplittable) {
+          object = object.split('');
+        } else {
+          object = [];
+          while (++index < length) {
+            // in Safari 2 `index in object` is always `false` for NodeLists
+            object[index] = isSnapshot ? result.snapshotItem(index) : result[index];
+          }
+        }
+      }
+      forEach(object, callback, thisArg);
+    } else {
+      forOwn(object, callback, thisArg);
+    }
+    return result;
+  }
+
+  /**
+   * Copies enumerable properties from the source(s) object to the destination object.
+   *
+   * @static
+   * @memberOf Benchmark
+   * @param {Object} destination The destination object.
+   * @param {Object} [source={}] The source object.
+   * @returns {Object} The destination object.
+   */
+  function extend(destination, source) {
+    // Chrome < 14 incorrectly sets `destination` to `undefined` when we `delete arguments[0]`
+    // http://code.google.com/p/v8/issues/detail?id=839
+    var result = destination;
+    delete arguments[0];
+
+    forEach(arguments, function(source) {
+      forProps(source, function(value, key) {
+        result[key] = value;
+      });
+    });
+    return result;
+  }
+
+  /**
+   * A generic `Array#filter` like method.
+   *
+   * @static
+   * @memberOf Benchmark
+   * @param {Array} array The array to iterate over.
+   * @param {Function|String} callback The function/alias called per iteration.
+   * @param {Mixed} thisArg The `this` binding for the callback.
+   * @returns {Array} A new array of values that passed callback filter.
+   * @example
+   *
+   * // get odd numbers
+   * Benchmark.filter([1, 2, 3, 4, 5], function(n) {
+   *   return n % 2;
+   * }); // -> [1, 3, 5];
+   *
+   * // get fastest benchmarks
+   * Benchmark.filter(benches, 'fastest');
+   *
+   * // get slowest benchmarks
+   * Benchmark.filter(benches, 'slowest');
+   *
+   * // get benchmarks that completed without erroring
+   * Benchmark.filter(benches, 'successful');
+   */
+  function filter(array, callback, thisArg) {
+    var result;
+
+    if (callback == 'successful') {
+      // callback to exclude those that are errored, unrun, or have hz of Infinity
+      callback = function(bench) { return bench.cycles && isFinite(bench.hz); };
+    }
+    else if (callback == 'fastest' || callback == 'slowest') {
+      // get successful, sort by period + margin of error, and filter fastest/slowest
+      result = filter(array, 'successful').sort(function(a, b) {
+        a = a.stats; b = b.stats;
+        return (a.mean + a.moe > b.mean + b.moe ? 1 : -1) * (callback == 'fastest' ? 1 : -1);
+      });
+      result = filter(result, function(bench) {
+        return result[0].compare(bench) == 0;
+      });
+    }
+    return result || reduce(array, function(result, value, index) {
+      return callback.call(thisArg, value, index, array) ? (result.push(value), result) : result;
+    }, []);
+  }
+
+  /**
+   * A generic `Array#forEach` like method.
+   * Callbacks may terminate the loop by explicitly returning `false`.
+   *
+   * @static
+   * @memberOf Benchmark
+   * @param {Array} array The array to iterate over.
+   * @param {Function} callback The function called per iteration.
+   * @param {Mixed} thisArg The `this` binding for the callback.
+   * @returns {Array} Returns the array iterated over.
+   */
+  function forEach(array, callback, thisArg) {
+    var index = -1,
+        length = (array = Object(array)).length >>> 0;
+
+    if (thisArg !== undefined) {
+      callback = bind(callback, thisArg);
+    }
+    while (++index < length) {
+      if (index in array &&
+          callback(array[index], index, array) === false) {
+        break;
+      }
+    }
+    return array;
+  }
+
+  /**
+   * Iterates over an object's own properties, executing the `callback` for each.
+   * Callbacks may terminate the loop by explicitly returning `false`.
+   *
+   * @static
+   * @memberOf Benchmark
+   * @param {Object} object The object to iterate over.
+   * @param {Function} callback The function executed per own property.
+   * @param {Mixed} thisArg The `this` binding for the callback.
+   * @returns {Object} Returns the object iterated over.
+   */
+  function forOwn(object, callback, thisArg) {
+    return forProps(object, callback, { 'bind': thisArg, 'which': 'own' });
+  }
+
+  /**
+   * Converts a number to a more readable comma-separated string representation.
+   *
+   * @static
+   * @memberOf Benchmark
+   * @param {Number} number The number to convert.
+   * @returns {String} The more readable string representation.
+   */
+  function formatNumber(number) {
+    number = String(number).split('.');
+    return number[0].replace(/(?=(?:\d{3})+$)(?!\b)/g, ',') +
+      (number[1] ? '.' + number[1] : '');
+  }
+
+  /**
+   * Checks if an object has the specified key as a direct property.
+   *
+   * @static
+   * @memberOf Benchmark
+   * @param {Object} object The object to check.
+   * @param {String} key The key to check for.
+   * @returns {Boolean} Returns `true` if key is a direct property, else `false`.
+   */
+  function hasKey() {
+    // lazy define for worst case fallback (not as accurate)
+    hasKey = function(object, key) {
+      var parent = object != null && (object.constructor || Object).prototype;
+      return !!parent && key in Object(object) && !(key in parent && object[key] === parent[key]);
+    };
+    // for modern browsers
+    if (isClassOf(hasOwnProperty, 'Function')) {
+      hasKey = function(object, key) {
+        return object != null && hasOwnProperty.call(object, key);
+      };
+    }
+    // for Safari 2
+    else if ({}.__proto__ == Object.prototype) {
+      hasKey = function(object, key) {
+        var result = false;
+        if (object != null) {
+          object = Object(object);
+          object.__proto__ = [object.__proto__, object.__proto__ = null, result = key in object][0];
+        }
+        return result;
+      };
+    }
+    return hasKey.apply(this, arguments);
+  }
+
+  /**
+   * A generic `Array#indexOf` like method.
+   *
+   * @static
+   * @memberOf Benchmark
+   * @param {Array} array The array to iterate over.
+   * @param {Mixed} value The value to search for.
+   * @param {Number} [fromIndex=0] The index to start searching from.
+   * @returns {Number} The index of the matched value or `-1`.
+   */
+  function indexOf(array, value, fromIndex) {
+    var index = toInteger(fromIndex),
+        length = (array = Object(array)).length >>> 0;
+
+    index = (index < 0 ? max(0, length + index) : index) - 1;
+    while (++index < length) {
+      if (index in array && value === array[index]) {
+        return index;
+      }
+    }
+    return -1;
+  }
+
+  /**
+   * Modify a string by replacing named tokens with matching object property values.
+   *
+   * @static
+   * @memberOf Benchmark
+   * @param {String} string The string to modify.
+   * @param {Object} object The template object.
+   * @returns {String} The modified string.
+   */
+  function interpolate(string, object) {
+    forOwn(object, function(value, key) {
+      // escape regexp special characters in `key`
+      string = string.replace(RegExp('#\\{' + key.replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1') + '\\}', 'g'), value);
+    });
+    return string;
+  }
+
+  /**
+   * Invokes a method on all items in an array.
+   *
+   * @static
+   * @memberOf Benchmark
+   * @param {Array} benches Array of benchmarks to iterate over.
+   * @param {String|Object} name The name of the method to invoke OR options object.
+   * @param {Mixed} [arg1, arg2, ...] Arguments to invoke the method with.
+   * @returns {Array} A new array of values returned from each method invoked.
+   * @example
+   *
+   * // invoke `reset` on all benchmarks
+   * Benchmark.invoke(benches, 'reset');
+   *
+   * // invoke `emit` with arguments
+   * Benchmark.invoke(benches, 'emit', 'complete', listener);
+   *
+   * // invoke `run(true)`, treat benchmarks as a queue, and register invoke callbacks
+   * Benchmark.invoke(benches, {
+   *
+   *   // invoke the `run` method
+   *   'name': 'run',
+   *
+   *   // pass a single argument
+   *   'args': true,
+   *
+   *   // treat as queue, removing benchmarks from front of `benches` until empty
+   *   'queued': true,
+   *
+   *   // called before any benchmarks have been invoked.
+   *   'onStart': onStart,
+   *
+   *   // called between invoking benchmarks
+   *   'onCycle': onCycle,
+   *
+   *   // called after all benchmarks have been invoked.
+   *   'onComplete': onComplete
+   * });
+   */
+  function invoke(benches, name) {
+    var args,
+        bench,
+        queued,
+        index = -1,
+        eventProps = { 'currentTarget': benches },
+        options = { 'onStart': noop, 'onCycle': noop, 'onComplete': noop },
+        result = map(benches, function(bench) { return bench; });
+
+    /**
+     * Invokes the method of the current object and if synchronous, fetches the next.
+     */
+    function execute() {
+      var listeners,
+          async = isAsync(bench);
+
+      if (async) {
+        // use `getNext` as the first listener
+        bench.on('complete', getNext);
+        listeners = bench.events.complete;
+        listeners.splice(0, 0, listeners.pop());
+      }
+      // execute method
+      result[index] = isClassOf(bench && bench[name], 'Function') ? bench[name].apply(bench, args) : undefined;
+      // if synchronous return true until finished
+      return !async && getNext();
+    }
+
+    /**
+     * Fetches the next bench or executes `onComplete` callback.
+     */
+    function getNext(event) {
+      var cycleEvent,
+          last = bench,
+          async = isAsync(last);
+
+      if (async) {
+        last.off('complete', getNext);
+        last.emit('complete');
+      }
+      // emit "cycle" event
+      eventProps.type = 'cycle';
+      eventProps.target = last;
+      cycleEvent = Event(eventProps);
+      options.onCycle.call(benches, cycleEvent);
+
+      // choose next benchmark if not exiting early
+      if (!cycleEvent.aborted && raiseIndex() !== false) {
+        bench = queued ? benches[0] : result[index];
+        if (isAsync(bench)) {
+          delay(bench, execute);
+        }
+        else if (async) {
+          // resume execution if previously asynchronous but now synchronous
+          while (execute()) { }
+        }
+        else {
+          // continue synchronous execution
+          return true;
+        }
+      } else {
+        // emit "complete" event
+        eventProps.type = 'complete';
+        options.onComplete.call(benches, Event(eventProps));
+      }
+      // When used as a listener `event.aborted = true` will cancel the rest of
+      // the "complete" listeners because they were already called above and when
+      // used as part of `getNext` the `return false` will exit the execution while-loop.
+      if (event) {
+        event.aborted = true;
+      } else {
+        return false;
+      }
+    }
+
+    /**
+     * Checks if invoking `Benchmark#run` with asynchronous cycles.
+     */
+    function isAsync(object) {
+      // avoid using `instanceof` here because of IE memory leak issues with host objects
+      var async = args[0] && args[0].async;
+      return Object(object).constructor == Benchmark && name == 'run' &&
+        ((async == null ? object.options.async : async) && support.timeout || object.defer);
+    }
+
+    /**
+     * Raises `index` to the next defined index or returns `false`.
+     */
+    function raiseIndex() {
+      var length = result.length;
+      if (queued) {
+        // if queued remove the previous bench and subsequent skipped non-entries
+        do {
+          ++index > 0 && shift.call(benches);
+        } while ((length = benches.length) && !('0' in benches));
+      }
+      else {
+        while (++index < length && !(index in result)) { }
+      }
+      // if we reached the last index then return `false`
+      return (queued ? length : index < length) ? index : (index = false);
+    }
+
+    // juggle arguments
+    if (isClassOf(name, 'String')) {
+      // 2 arguments (array, name)
+      args = slice.call(arguments, 2);
+    } else {
+      // 2 arguments (array, options)
+      options = extend(options, name);
+      name = options.name;
+      args = isClassOf(args = 'args' in options ? options.args : [], 'Array') ? args : [args];
+      queued = options.queued;
+    }
+
+    // start iterating over the array
+    if (raiseIndex() !== false) {
+      // emit "start" event
+      bench = result[index];
+      eventProps.type = 'start';
+      eventProps.target = bench;
+      options.onStart.call(benches, Event(eventProps));
+
+      // end early if the suite was aborted in an "onStart" listener
+      if (benches.aborted && benches.constructor == Suite && name == 'run') {
+        // emit "cycle" event
+        eventProps.type = 'cycle';
+        options.onCycle.call(benches, Event(eventProps));
+        // emit "complete" event
+        eventProps.type = 'complete';
+        options.onComplete.call(benches, Event(eventProps));
+      }
+      // else start
+      else {
+        if (isAsync(bench)) {
+          delay(bench, execute);
+        } else {
+          while (execute()) { }
+        }
+      }
+    }
+    return result;
+  }
+
+  /**
+   * Creates a string of joined array values or object key-value pairs.
+   *
+   * @static
+   * @memberOf Benchmark
+   * @param {Array|Object} object The object to operate on.
+   * @param {String} [separator1=','] The separator used between key-value pairs.
+   * @param {String} [separator2=': '] The separator used between keys and values.
+   * @returns {String} The joined result.
+   */
+  function join(object, separator1, separator2) {
+    var result = [],
+        length = (object = Object(object)).length,
+        arrayLike = length === length >>> 0;
+
+    separator2 || (separator2 = ': ');
+    each(object, function(value, key) {
+      result.push(arrayLike ? value : key + separator2 + value);
+    });
+    return result.join(separator1 || ',');
+  }
+
+  /**
+   * A generic `Array#map` like method.
+   *
+   * @static
+   * @memberOf Benchmark
+   * @param {Array} array The array to iterate over.
+   * @param {Function} callback The function called per iteration.
+   * @param {Mixed} thisArg The `this` binding for the callback.
+   * @returns {Array} A new array of values returned by the callback.
+   */
+  function map(array, callback, thisArg) {
+    return reduce(array, function(result, value, index) {
+      result[index] = callback.call(thisArg, value, index, array);
+      return result;
+    }, Array(Object(array).length >>> 0));
+  }
+
+  /**
+   * Retrieves the value of a specified property from all items in an array.
+   *
+   * @static
+   * @memberOf Benchmark
+   * @param {Array} array The array to iterate over.
+   * @param {String} property The property to pluck.
+   * @returns {Array} A new array of property values.
+   */
+  function pluck(array, property) {
+    return map(array, function(object) {
+      return object == null ? undefined : object[property];
+    });
+  }
+
+  /**
+   * A generic `Array#reduce` like method.
+   *
+   * @static
+   * @memberOf Benchmark
+   * @param {Array} array The array to iterate over.
+   * @param {Function} callback The function called per iteration.
+   * @param {Mixed} accumulator Initial value of the accumulator.
+   * @returns {Mixed} The accumulator.
+   */
+  function reduce(array, callback, accumulator) {
+    var noaccum = arguments.length < 3;
+    forEach(array, function(value, index) {
+      accumulator = noaccum ? (noaccum = false, value) : callback(accumulator, value, index, array);
+    });
+    return accumulator;
+  }
+
+  /*--------------------------------------------------------------------------*/
+
+  /**
+   * Aborts all benchmarks in the suite.
+   *
+   * @name abort
+   * @memberOf Benchmark.Suite
+   * @returns {Object} The suite instance.
+   */
+  function abortSuite() {
+    var event,
+        me = this,
+        resetting = calledBy.resetSuite;
+
+    if (me.running) {
+      event = Event('abort');
+      me.emit(event);
+      if (!event.cancelled || resetting) {
+        // avoid infinite recursion
+        calledBy.abortSuite = true;
+        me.reset();
+        delete calledBy.abortSuite;
+
+        if (!resetting) {
+          me.aborted = true;
+          invoke(me, 'abort');
+        }
+      }
+    }
+    return me;
+  }
+
+  /**
+   * Adds a test to the benchmark suite.
+   *
+   * @memberOf Benchmark.Suite
+   * @param {String} name A name to identify the benchmark.
+   * @param {Function|String} fn The test to benchmark.
+   * @param {Object} [options={}] Options object.
+   * @returns {Object} The benchmark instance.
+   * @example
+   *
+   * // basic usage
+   * suite.add(fn);
+   *
+   * // or using a name first
+   * suite.add('foo', fn);
+   *
+   * // or with options
+   * suite.add('foo', fn, {
+   *   'onCycle': onCycle,
+   *   'onComplete': onComplete
+   * });
+   *
+   * // or name and options
+   * suite.add('foo', {
+   *   'fn': fn,
+   *   'onCycle': onCycle,
+   *   'onComplete': onComplete
+   * });
+   *
+   * // or options only
+   * suite.add({
+   *   'name': 'foo',
+   *   'fn': fn,
+   *   'onCycle': onCycle,
+   *   'onComplete': onComplete
+   * });
+   */
+  function add(name, fn, options) {
+    var me = this,
+        bench = Benchmark(name, fn, options),
+        event = Event({ 'type': 'add', 'target': bench });
+
+    if (me.emit(event), !event.cancelled) {
+      me.push(bench);
+    }
+    return me;
+  }
+
+  /**
+   * Creates a new suite with cloned benchmarks.
+   *
+   * @name clone
+   * @memberOf Benchmark.Suite
+   * @param {Object} options Options object to overwrite cloned options.
+   * @returns {Object} The new suite instance.
+   */
+  function cloneSuite(options) {
+    var me = this,
+        result = new me.constructor(extend({}, me.options, options));
+
+    // copy own properties
+    forOwn(me, function(value, key) {
+      if (!hasKey(result, key)) {
+        result[key] = value && isClassOf(value.clone, 'Function')
+          ? value.clone()
+          : deepClone(value);
+      }
+    });
+    return result;
+  }
+
+  /**
+   * An `Array#filter` like method.
+   *
+   * @name filter
+   * @memberOf Benchmark.Suite
+   * @param {Function|String} callback The function/alias called per iteration.
+   * @returns {Object} A new suite of benchmarks that passed callback filter.
+   */
+  function filterSuite(callback) {
+    var me = this,
+        result = new me.constructor;
+
+    result.push.apply(result, filter(me, callback));
+    return result;
+  }
+
+  /**
+   * Resets all benchmarks in the suite.
+   *
+   * @name reset
+   * @memberOf Benchmark.Suite
+   * @returns {Object} The suite instance.
+   */
+  function resetSuite() {
+    var event,
+        me = this,
+        aborting = calledBy.abortSuite;
+
+    if (me.running && !aborting) {
+      // no worries, `resetSuite()` is called within `abortSuite()`
+      calledBy.resetSuite = true;
+      me.abort();
+      delete calledBy.resetSuite;
+    }
+    // reset if the state has changed
+    else if ((me.aborted || me.running) &&
+        (me.emit(event = Event('reset')), !event.cancelled)) {
+      me.running = false;
+      if (!aborting) {
+        invoke(me, 'reset');
+      }
+    }
+    return me;
+  }
+
+  /**
+   * Runs the suite.
+   *
+   * @name run
+   * @memberOf Benchmark.Suite
+   * @param {Object} [options={}] Options object.
+   * @returns {Object} The suite instance.
+   * @example
+   *
+   * // basic usage
+   * suite.run();
+   *
+   * // or with options
+   * suite.run({ 'async': true, 'queued': true });
+   */
+  function runSuite(options) {
+    var me = this;
+
+    me.reset();
+    me.running = true;
+    options || (options = {});
+
+    invoke(me, {
+      'name': 'run',
+      'args': options,
+      'queued': options.queued,
+      'onStart': function(event) {
+        me.emit(event);
+      },
+      'onCycle': function(event) {
+        var bench = event.target;
+        if (bench.error) {
+          me.emit({ 'type': 'error', 'target': bench });
+        }
+        me.emit(event);
+        event.aborted = me.aborted;
+      },
+      'onComplete': function(event) {
+        me.running = false;
+        me.emit(event);
+      }
+    });
+    return me;
+  }
+
+  /*--------------------------------------------------------------------------*/
+
+  /**
+   * Executes all registered listeners of the specified event type.
+   *
+   * @memberOf Benchmark, Benchmark.Suite
+   * @param {String|Object} type The event type or object.
+   * @returns {Mixed} Returns the return value of the last listener executed.
+   */
+  function emit(type) {
+    var listeners,
+        me = this,
+        event = Event(type),
+        events = me.events,
+        args = (arguments[0] = event, arguments);
+
+    event.currentTarget || (event.currentTarget = me);
+    event.target || (event.target = me);
+    delete event.result;
+
+    if (events && (listeners = hasKey(events, event.type) && events[event.type])) {
+      forEach(listeners.slice(), function(listener) {
+        if ((event.result = listener.apply(me, args)) === false) {
+          event.cancelled = true;
+        }
+        return !event.aborted;
+      });
+    }
+    return event.result;
+  }
+
+  /**
+   * Returns an array of event listeners for a given type that can be manipulated
+   * to add or remove listeners.
+   *
+   * @memberOf Benchmark, Benchmark.Suite
+   * @param {String} type The event type.
+   * @returns {Array} The listeners array.
+   */
+  function listeners(type) {
+    var me = this,
+        events = me.events || (me.events = {});
+
+    return hasKey(events, type) ? events[type] : (events[type] = []);
+  }
+
+  /**
+   * Unregisters a listener for the specified event type(s),
+   * or unregisters all listeners for the specified event type(s),
+   * or unregisters all listeners for all event types.
+   *
+   * @memberOf Benchmark, Benchmark.Suite
+   * @param {String} [type] The event type.
+   * @param {Function} [listener] The function to unregister.
+   * @returns {Object} The benchmark instance.
+   * @example
+   *
+   * // unregister a listener for an event type
+   * bench.off('cycle', listener);
+   *
+   * // unregister a listener for multiple event types
+   * bench.off('start cycle', listener);
+   *
+   * // unregister all listeners for an event type
+   * bench.off('cycle');
+   *
+   * // unregister all listeners for multiple event types
+   * bench.off('start cycle complete');
+   *
+   * // unregister all listeners for all event types
+   * bench.off();
+   */
+  function off(type, listener) {
+    var me = this,
+        events = me.events;
+
+    events && each(type ? type.split(' ') : events, function(listeners, type) {
+      var index;
+      if (typeof listeners == 'string') {
+        type = listeners;
+        listeners = hasKey(events, type) && events[type];
+      }
+      if (listeners) {
+        if (listener) {
+          index = indexOf(listeners, listener);
+          if (index > -1) {
+            listeners.splice(index, 1);
+          }
+        } else {
+          listeners.length = 0;
+        }
+      }
+    });
+    return me;
+  }
+
+  /**
+   * Registers a listener for the specified event type(s).
+   *
+   * @memberOf Benchmark, Benchmark.Suite
+   * @param {String} type The event type.
+   * @param {Function} listener The function to register.
+   * @returns {Object} The benchmark instance.
+   * @example
+   *
+   * // register a listener for an event type
+   * bench.on('cycle', listener);
+   *
+   * // register a listener for multiple event types
+   * bench.on('start cycle', listener);
+   */
+  function on(type, listener) {
+    var me = this,
+        events = me.events || (me.events = {});
+
+    forEach(type.split(' '), function(type) {
+      (hasKey(events, type)
+        ? events[type]
+        : (events[type] = [])
+      ).push(listener);
+    });
+    return me;
+  }
+
+  /*--------------------------------------------------------------------------*/
+
+  /**
+   * Aborts the benchmark without recording times.
+   *
+   * @memberOf Benchmark
+   * @returns {Object} The benchmark instance.
+   */
+  function abort() {
+    var event,
+        me = this,
+        resetting = calledBy.reset;
+
+    if (me.running) {
+      event = Event('abort');
+      me.emit(event);
+      if (!event.cancelled || resetting) {
+        // avoid infinite recursion
+        calledBy.abort = true;
+        me.reset();
+        delete calledBy.abort;
+
+        if (support.timeout) {
+          clearTimeout(me._timerId);
+          delete me._timerId;
+        }
+        if (!resetting) {
+          me.aborted = true;
+          me.running = false;
+        }
+      }
+    }
+    return me;
+  }
+
+  /**
+   * Creates a new benchmark using the same test and options.
+   *
+   * @memberOf Benchmark
+   * @param {Object} options Options object to overwrite cloned options.
+   * @returns {Object} The new benchmark instance.
+   * @example
+   *
+   * var bizarro = bench.clone({
+   *   'name': 'doppelganger'
+   * });
+   */
+  function clone(options) {
+    var me = this,
+        result = new me.constructor(extend({}, me, options));
+
+    // correct the `options` object
+    result.options = extend({}, me.options, options);
+
+    // copy own custom properties
+    forOwn(me, function(value, key) {
+      if (!hasKey(result, key)) {
+        result[key] = deepClone(value);
+      }
+    });
+    return result;
+  }
+
+  /**
+   * Determines if a benchmark is faster than another.
+   *
+   * @memberOf Benchmark
+   * @param {Object} other The benchmark to compare.
+   * @returns {Number} Returns `-1` if slower, `1` if faster, and `0` if indeterminate.
+   */
+  function compare(other) {
+    var critical,
+        zStat,
+        me = this,
+        sample1 = me.stats.sample,
+        sample2 = other.stats.sample,
+        size1 = sample1.length,
+        size2 = sample2.length,
+        maxSize = max(size1, size2),
+        minSize = min(size1, size2),
+        u1 = getU(sample1, sample2),
+        u2 = getU(sample2, sample1),
+        u = min(u1, u2);
+
+    function getScore(xA, sampleB) {
+      return reduce(sampleB, function(total, xB) {
+        return total + (xB > xA ? 0 : xB < xA ? 1 : 0.5);
+      }, 0);
+    }
+
+    function getU(sampleA, sampleB) {
+      return reduce(sampleA, function(total, xA) {
+        return total + getScore(xA, sampleB);
+      }, 0);
+    }
+
+    function getZ(u) {
+      return (u - ((size1 * size2) / 2)) / sqrt((size1 * size2 * (size1 + size2 + 1)) / 12);
+    }
+
+    // exit early if comparing the same benchmark
+    if (me == other) {
+      return 0;
+    }
+    // reject the null hyphothesis the two samples come from the
+    // same population (i.e. have the same median) if...
+    if (size1 + size2 > 30) {
+      // ...the z-stat is greater than 1.96 or less than -1.96
+      // http://www.statisticslectures.com/topics/mannwhitneyu/
+      zStat = getZ(u);
+      return abs(zStat) > 1.96 ? (zStat > 0 ? -1 : 1) : 0;
+    }
+    // ...the U value is less than or equal the critical U value
+    // http://www.geoib.com/mann-whitney-u-test.html
+    critical = maxSize < 5 || minSize < 3 ? 0 : uTable[maxSize][minSize - 3];
+    return u <= critical ? (u == u1 ? 1 : -1) : 0;
+  }
+
+  /**
+   * Reset properties and abort if running.
+   *
+   * @memberOf Benchmark
+   * @returns {Object} The benchmark instance.
+   */
+  function reset() {
+    var data,
+        event,
+        me = this,
+        index = 0,
+        changes = { 'length': 0 },
+        queue = { 'length': 0 };
+
+    if (me.running && !calledBy.abort) {
+      // no worries, `reset()` is called within `abort()`
+      calledBy.reset = true;
+      me.abort();
+      delete calledBy.reset;
+    }
+    else {
+      // a non-recursive solution to check if properties have changed
+      // http://www.jslab.dk/articles/non.recursive.preorder.traversal.part4
+      data = { 'destination': me, 'source': extend({}, me.constructor.prototype, me.options) };
+      do {
+        forOwn(data.source, function(value, key) {
+          var changed,
+              destination = data.destination,
+              currValue = destination[key];
+
+          if (value && typeof value == 'object') {
+            if (isClassOf(value, 'Array')) {
+              // check if an array value has changed to a non-array value
+              if (!isClassOf(currValue, 'Array')) {
+                changed = currValue = [];
+              }
+              // or has changed its length
+              if (currValue.length != value.length) {
+                changed = currValue = currValue.slice(0, value.length);
+                currValue.length = value.length;
+              }
+            }
+            // check if an object has changed to a non-object value
+            else if (!currValue || typeof currValue != 'object') {
+              changed = currValue = {};
+            }
+            // register a changed object
+            if (changed) {
+              changes[changes.length++] = { 'destination': destination, 'key': key, 'value': currValue };
+            }
+            queue[queue.length++] = { 'destination': currValue, 'source': value };
+          }
+          // register a changed primitive
+          else if (value !== currValue && !(value == null || isClassOf(value, 'Function'))) {
+            changes[changes.length++] = { 'destination': destination, 'key': key, 'value': value };
+          }
+        });
+      }
+      while ((data = queue[index++]));
+
+      // if changed emit the `reset` event and if it isn't cancelled reset the benchmark
+      if (changes.length && (me.emit(event = Event('reset')), !event.cancelled)) {
+        forEach(changes, function(data) {
+          data.destination[data.key] = data.value;
+        });
+      }
+    }
+    return me;
+  }
+
+  /**
+   * Displays relevant benchmark information when coerced to a string.
+   *
+   * @name toString
+   * @memberOf Benchmark
+   * @returns {String} A string representation of the benchmark instance.
+   */
+  function toStringBench() {
+    var me = this,
+        error = me.error,
+        hz = me.hz,
+        id = me.id,
+        stats = me.stats,
+        size = stats.sample.length,
+        pm = support.java ? '+/-' : '\xb1',
+        result = me.name || (isNaN(id) ? id : '');
+
+    if (error) {
+      result += ': ' + join(error);
+    } else {
+      result += ' x ' + formatNumber(hz.toFixed(hz < 100 ? 2 : 0)) + ' ops/sec ' + pm +
+        stats.rme.toFixed(2) + '% (' + size + ' run' + (size == 1 ? '' : 's') + ' sampled)';
+    }
+    return result;
+  }
+
+  /*--------------------------------------------------------------------------*/
+
+  /**
+   * Clocks the time taken to execute a test per cycle (secs).
+   *
+   * @private
+   * @param {Object} bench The benchmark instance.
+   * @returns {Number} The time taken.
+   */
+  function clock() {
+    var applet,
+        options = Benchmark.options,
+        template = { 'begin': 's$=new n$', 'end': 'r$=(new n$-s$)/1e3', 'uid': uid },
+        timers = [{ 'ns': timer.ns, 'res': max(0.0015, getRes('ms')), 'unit': 'ms' }];
+
+    // lazy define for hi-res timers
+    clock = function(clone) {
+      var deferred;
+      if (clone instanceof Deferred) {
+        deferred = clone;
+        clone = deferred.benchmark;
+      }
+
+      var bench = clone._original,
+          fn = bench.fn,
+          fnArg = deferred ? getFirstArgument(fn) || 'deferred' : '',
+          stringable = isStringable(fn);
+
+      var source = {
+        'setup': getSource(bench.setup, preprocess('m$.setup()')),
+        'fn': getSource(fn, preprocess('m$.fn(' + fnArg + ')')),
+        'fnArg': fnArg,
+        'teardown': getSource(bench.teardown, preprocess('m$.teardown()'))
+      };
+
+      var count = bench.count = clone.count,
+          decompilable = support.decompilation || stringable,
+          id = bench.id,
+          isEmpty = !(source.fn || stringable),
+          name = bench.name || (typeof id == 'number' ? '' : id),
+          ns = timer.ns,
+          result = 0;
+
+      // init `minTime` if needed
+      clone.minTime = bench.minTime || (bench.minTime = bench.options.minTime = options.minTime);
+
+      // repair nanosecond timer
+      // (some Chrome builds erase the `ns` variable after millions of executions)
+      if (applet) {
+        try {
+          ns.nanoTime();
+        } catch(e) {
+          // use non-element to avoid issues with libs that augment them
+          ns = timer.ns = new applet.Packages.nano;
+        }
+      }
+
+      // Compile in setup/teardown functions and the test loop.
+      // Create a new compiled test, instead of using the cached `bench.compiled`,
+      // to avoid potential engine optimizations enabled over the life of the test.
+      var compiled = bench.compiled = createFunction(preprocess('t$'), interpolate(
+        preprocess(deferred
+          ? 'var d$=this,#{fnArg}=d$,m$=d$.benchmark._original,f$=m$.fn,su$=m$.setup,td$=m$.teardown;' +
+            // when `deferred.cycles` is `0` then...
+            'if(!d$.cycles){' +
+            // set `deferred.fn`
+            'd$.fn=function(){var #{fnArg}=d$;if(typeof f$=="function"){try{#{fn}\n}catch(e$){f$(d$)}}else{#{fn}\n}};' +
+            // set `deferred.teardown`
+            'd$.teardown=function(){d$.cycles=0;if(typeof td$=="function"){try{#{teardown}\n}catch(e$){td$()}}else{#{teardown}\n}};' +
+            // execute the benchmark's `setup`
+            'if(typeof su$=="function"){try{#{setup}\n}catch(e$){su$()}}else{#{setup}\n};' +
+            // start timer
+            't$.start(d$);' +
+            // execute `deferred.fn` and return a dummy object
+            '}d$.fn();return{}'
+
+          : 'var r$,s$,m$=this,f$=m$.fn,i$=m$.count,n$=t$.ns;#{setup}\n#{begin};' +
+            'while(i$--){#{fn}\n}#{end};#{teardown}\nreturn{elapsed:r$,uid:"#{uid}"}'),
+        source
+      ));
+
+      try {
+        if (isEmpty) {
+          // Firefox may remove dead code from Function#toString results
+          // http://bugzil.la/536085
+          throw new Error('The test "' + name + '" is empty. This may be the result of dead code removal.');
+        }
+        else if (!deferred) {
+          // pretest to determine if compiled code is exits early, usually by a
+          // rogue `return` statement, by checking for a return object with the uid
+          bench.count = 1;
+          compiled = (compiled.call(bench, timer) || {}).uid == uid && compiled;
+          bench.count = count;
+        }
+      } catch(e) {
+        compiled = null;
+        clone.error = e || new Error(String(e));
+        bench.count = count;
+      }
+      // fallback when a test exits early or errors during pretest
+      if (decompilable && !compiled && !deferred && !isEmpty) {
+        compiled = createFunction(preprocess('t$'), interpolate(
+          preprocess(
+            (clone.error && !stringable
+              ? 'var r$,s$,m$=this,f$=m$.fn,i$=m$.count'
+              : 'function f$(){#{fn}\n}var r$,s$,m$=this,i$=m$.count'
+            ) +
+            ',n$=t$.ns;#{setup}\n#{begin};m$.f$=f$;while(i$--){m$.f$()}#{end};' +
+            'delete m$.f$;#{teardown}\nreturn{elapsed:r$}'
+          ),
+          source
+        ));
+
+        try {
+          // pretest one more time to check for errors
+          bench.count = 1;
+          compiled.call(bench, timer);
+          bench.compiled = compiled;
+          bench.count = count;
+          delete clone.error;
+        }
+        catch(e) {
+          bench.count = count;
+          if (clone.error) {
+            compiled = null;
+          } else {
+            bench.compiled = compiled;
+            clone.error = e || new Error(String(e));
+          }
+        }
+      }
+      // assign `compiled` to `clone` before calling in case a deferred benchmark
+      // immediately calls `deferred.resolve()`
+      clone.compiled = compiled;
+      // if no errors run the full test loop
+      if (!clone.error) {
+        result = compiled.call(deferred || bench, timer).elapsed;
+      }
+      return result;
+    };
+
+    /*------------------------------------------------------------------------*/
+
+    /**
+     * Gets the current timer's minimum resolution (secs).
+     */
+    function getRes(unit) {
+      var measured,
+          begin,
+          count = 30,
+          divisor = 1e3,
+          ns = timer.ns,
+          sample = [];
+
+      // get average smallest measurable time
+      while (count--) {
+        if (unit == 'us') {
+          divisor = 1e6;
+          if (ns.stop) {
+            ns.start();
+            while (!(measured = ns.microseconds())) { }
+          } else if (ns[perfName]) {
+            divisor = 1e3;
+            measured = Function('n', 'var r,s=n.' + perfName + '();while(!(r=n.' + perfName + '()-s)){};return r')(ns);
+          } else {
+            begin = ns();
+            while (!(measured = ns() - begin)) { }
+          }
+        }
+        else if (unit == 'ns') {
+          divisor = 1e9;
+          if (ns.nanoTime) {
+            begin = ns.nanoTime();
+            while (!(measured = ns.nanoTime() - begin)) { }
+          } else {
+            begin = (begin = ns())[0] + (begin[1] / divisor);
+            while (!(measured = ((measured = ns())[0] + (measured[1] / divisor)) - begin)) { }
+            divisor = 1;
+          }
+        }
+        else {
+          begin = new ns;
+          while (!(measured = new ns - begin)) { }
+        }
+        // check for broken timers (nanoTime may have issues)
+        // http://alivebutsleepy.srnet.cz/unreliable-system-nanotime/
+        if (measured > 0) {
+          sample.push(measured);
+        } else {
+          sample.push(Infinity);
+          break;
+        }
+      }
+      // convert to seconds
+      return getMean(sample) / divisor;
+    }
+
+    /**
+     * Replaces all occurrences of `$` with a unique number and
+     * template tokens with content.
+     */
+    function preprocess(code) {
+      return interpolate(code, template).replace(/\$/g, /\d+/.exec(uid));
+    }
+
+    /*------------------------------------------------------------------------*/
+
+    // detect nanosecond support from a Java applet
+    each(doc && doc.applets || [], function(element) {
+      return !(timer.ns = applet = 'nanoTime' in element && element);
+    });
+
+    // check type in case Safari returns an object instead of a number
+    try {
+      if (typeof timer.ns.nanoTime() == 'number') {
+        timers.push({ 'ns': timer.ns, 'res': getRes('ns'), 'unit': 'ns' });
+      }
+    } catch(e) { }
+
+    // detect Chrome's microsecond timer:
+    // enable benchmarking via the --enable-benchmarking command
+    // line switch in at least Chrome 7 to use chrome.Interval
+    try {
+      if ((timer.ns = new (window.chrome || window.chromium).Interval)) {
+        timers.push({ 'ns': timer.ns, 'res': getRes('us'), 'unit': 'us' });
+      }
+    } catch(e) { }
+
+    // detect `performance.now` microsecond resolution timer
+    if ((timer.ns = perfName && perfObject)) {
+      timers.push({ 'ns': timer.ns, 'res': getRes('us'), 'unit': 'us' });
+    }
+
+    // detect Node's nanosecond resolution timer available in Node >= 0.8
+    if (processObject && typeof (timer.ns = processObject.hrtime) == 'function') {
+      timers.push({ 'ns': timer.ns, 'res': getRes('ns'), 'unit': 'ns' });
+    }
+
+    // detect Wade Simmons' Node microtime module
+    if (microtimeObject && typeof (timer.ns = microtimeObject.now) == 'function') {
+      timers.push({ 'ns': timer.ns,  'res': getRes('us'), 'unit': 'us' });
+    }
+
+    // pick timer with highest resolution
+    timer = reduce(timers, function(timer, other) {
+      return other.res < timer.res ? other : timer;
+    });
+
+    // remove unused applet
+    if (timer.unit != 'ns' && applet) {
+      applet = destroyElement(applet);
+    }
+    // error if there are no working timers
+    if (timer.res == Infinity) {
+      throw new Error('Benchmark.js was unable to find a working timer.');
+    }
+    // use API of chosen timer
+    if (timer.unit == 'ns') {
+      if (timer.ns.nanoTime) {
+        extend(template, {
+          'begin': 's$=n$.nanoTime()',
+          'end': 'r$=(n$.nanoTime()-s$)/1e9'
+        });
+      } else {
+        extend(template, {
+          'begin': 's$=n$()',
+          'end': 'r$=n$(s$);r$=r$[0]+(r$[1]/1e9)'
+        });
+      }
+    }
+    else if (timer.unit == 'us') {
+      if (timer.ns.stop) {
+        extend(template, {
+          'begin': 's$=n$.start()',
+          'end': 'r$=n$.microseconds()/1e6'
+        });
+      } else if (perfName) {
+        extend(template, {
+          'begin': 's$=n$.' + perfName + '()',
+          'end': 'r$=(n$.' + perfName + '()-s$)/1e3'
+        });
+      } else {
+        extend(template, {
+          'begin': 's$=n$()',
+          'end': 'r$=(n$()-s$)/1e6'
+        });
+      }
+    }
+
+    // define `timer` methods
+    timer.start = createFunction(preprocess('o$'),
+      preprocess('var n$=this.ns,#{begin};o$.elapsed=0;o$.timeStamp=s$'));
+
+    timer.stop = createFunction(preprocess('o$'),
+      preprocess('var n$=this.ns,s$=o$.timeStamp,#{end};o$.elapsed=r$'));
+
+    // resolve time span required to achieve a percent uncertainty of at most 1%
+    // http://spiff.rit.edu/classes/phys273/uncert/uncert.html
+    options.minTime || (options.minTime = max(timer.res / 2 / 0.01, 0.05));
+    return clock.apply(null, arguments);
+  }
+
+  /*--------------------------------------------------------------------------*/
+
+  /**
+   * Computes stats on benchmark results.
+   *
+   * @private
+   * @param {Object} bench The benchmark instance.
+   * @param {Object} options The options object.
+   */
+  function compute(bench, options) {
+    options || (options = {});
+
+    var async = options.async,
+        elapsed = 0,
+        initCount = bench.initCount,
+        minSamples = bench.minSamples,
+        queue = [],
+        sample = bench.stats.sample;
+
+    /**
+     * Adds a clone to the queue.
+     */
+    function enqueue() {
+      queue.push(bench.clone({
+        '_original': bench,
+        'events': {
+          'abort': [update],
+          'cycle': [update],
+          'error': [update],
+          'start': [update]
+        }
+      }));
+    }
+
+    /**
+     * Updates the clone/original benchmarks to keep their data in sync.
+     */
+    function update(event) {
+      var clone = this,
+          type = event.type;
+
+      if (bench.running) {
+        if (type == 'start') {
+          // Note: `clone.minTime` prop is inited in `clock()`
+          clone.count = bench.initCount;
+        }
+        else {
+          if (type == 'error') {
+            bench.error = clone.error;
+          }
+          if (type == 'abort') {
+            bench.abort();
+            bench.emit('cycle');
+          } else {
+            event.currentTarget = event.target = bench;
+            bench.emit(event);
+          }
+        }
+      } else if (bench.aborted) {
+        // clear abort listeners to avoid triggering bench's abort/cycle again
+        clone.events.abort.length = 0;
+        clone.abort();
+      }
+    }
+
+    /**
+     * Determines if more clones should be queued or if cycling should stop.
+     */
+    function evaluate(event) {
+      var critical,
+          df,
+          mean,
+          moe,
+          rme,
+          sd,
+          sem,
+          variance,
+          clone = event.target,
+          done = bench.aborted,
+          now = +new Date,
+          size = sample.push(clone.times.period),
+          maxedOut = size >= minSamples && (elapsed += now - clone.times.timeStamp) / 1e3 > bench.maxTime,
+          times = bench.times,
+          varOf = function(sum, x) { return sum + pow(x - mean, 2); };
+
+      // exit early for aborted or unclockable tests
+      if (done || clone.hz == Infinity) {
+        maxedOut = !(size = sample.length = queue.length = 0);
+      }
+
+      if (!done) {
+        // sample mean (estimate of the population mean)
+        mean = getMean(sample);
+        // sample variance (estimate of the population variance)
+        variance = reduce(sample, varOf, 0) / (size - 1) || 0;
+        // sample standard deviation (estimate of the population standard deviation)
+        sd = sqrt(variance);
+        // standard error of the mean (a.k.a. the standard deviation of the sampling distribution of the sample mean)
+        sem = sd / sqrt(size);
+        // degrees of freedom
+        df = size - 1;
+        // critical value
+        critical = tTable[Math.round(df) || 1] || tTable.infinity;
+        // margin of error
+        moe = sem * critical;
+        // relative margin of error
+        rme = (moe / mean) * 100 || 0;
+
+        extend(bench.stats, {
+          'deviation': sd,
+          'mean': mean,
+          'moe': moe,
+          'rme': rme,
+          'sem': sem,
+          'variance': variance
+        });
+
+        // Abort the cycle loop when the minimum sample size has been collected
+        // and the elapsed time exceeds the maximum time allowed per benchmark.
+        // We don't count cycle delays toward the max time because delays may be
+        // increased by browsers that clamp timeouts for inactive tabs.
+        // https://developer.mozilla.org/en/window.setTimeout#Inactive_tabs
+        if (maxedOut) {
+          // reset the `initCount` in case the benchmark is rerun
+          bench.initCount = initCount;
+          bench.running = false;
+          done = true;
+          times.elapsed = (now - times.timeStamp) / 1e3;
+        }
+        if (bench.hz != Infinity) {
+          bench.hz = 1 / mean;
+          times.cycle = mean * bench.count;
+          times.period = mean;
+        }
+      }
+      // if time permits, increase sample size to reduce the margin of error
+      if (queue.length < 2 && !maxedOut) {
+        enqueue();
+      }
+      // abort the invoke cycle when done
+      event.aborted = done;
+    }
+
+    // init queue and begin
+    enqueue();
+    invoke(queue, {
+      'name': 'run',
+      'args': { 'async': async },
+      'queued': true,
+      'onCycle': evaluate,
+      'onComplete': function() { bench.emit('complete'); }
+    });
+  }
+
+  /*--------------------------------------------------------------------------*/
+
+  /**
+   * Cycles a benchmark until a run `count` can be established.
+   *
+   * @private
+   * @param {Object} clone The cloned benchmark instance.
+   * @param {Object} options The options object.
+   */
+  function cycle(clone, options) {
+    options || (options = {});
+
+    var deferred;
+    if (clone instanceof Deferred) {
+      deferred = clone;
+      clone = clone.benchmark;
+    }
+
+    var clocked,
+        cycles,
+        divisor,
+        event,
+        minTime,
+        period,
+        async = options.async,
+        bench = clone._original,
+        count = clone.count,
+        times = clone.times;
+
+    // continue, if not aborted between cycles
+    if (clone.running) {
+      // `minTime` is set to `Benchmark.options.minTime` in `clock()`
+      cycles = ++clone.cycles;
+      clocked = deferred ? deferred.elapsed : clock(clone);
+      minTime = clone.minTime;
+
+      if (cycles > bench.cycles) {
+        bench.cycles = cycles;
+      }
+      if (clone.error) {
+        event = Event('error');
+        event.message = clone.error;
+        clone.emit(event);
+        if (!event.cancelled) {
+          clone.abort();
+        }
+      }
+    }
+
+    // continue, if not errored
+    if (clone.running) {
+      // time taken to complete last test cycle
+      bench.times.cycle = times.cycle = clocked;
+      // seconds per operation
+      period = bench.times.period = times.period = clocked / count;
+      // ops per second
+      bench.hz = clone.hz = 1 / period;
+      // avoid working our way up to this next time
+      bench.initCount = clone.initCount = count;
+      // do we need to do another cycle?
+      clone.running = clocked < minTime;
+
+      if (clone.running) {
+        // tests may clock at `0` when `initCount` is a small number,
+        // to avoid that we set its count to something a bit higher
+        if (!clocked && (divisor = divisors[clone.cycles]) != null) {
+          count = floor(4e6 / divisor);
+        }
+        // calculate how many more iterations it will take to achive the `minTime`
+        if (count <= clone.count) {
+          count += Math.ceil((minTime - clocked) / period);
+        }
+        clone.running = count != Infinity;
+      }
+    }
+    // should we exit early?
+    event = Event('cycle');
+    clone.emit(event);
+    if (event.aborted) {
+      clone.abort();
+    }
+    // figure out what to do next
+    if (clone.running) {
+      // start a new cycle
+      clone.count = count;
+      if (deferred) {
+        clone.compiled.call(deferred, timer);
+      } else if (async) {
+        delay(clone, function() { cycle(clone, options); });
+      } else {
+        cycle(clone);
+      }
+    }
+    else {
+      // fix TraceMonkey bug associated with clock fallbacks
+      // http://bugzil.la/509069
+      if (support.browser) {
+        runScript(uid + '=1;delete ' + uid);
+      }
+      // done
+      clone.emit('complete');
+    }
+  }
+
+  /*--------------------------------------------------------------------------*/
+
+  /**
+   * Runs the benchmark.
+   *
+   * @memberOf Benchmark
+   * @param {Object} [options={}] Options object.
+   * @returns {Object} The benchmark instance.
+   * @example
+   *
+   * // basic usage
+   * bench.run();
+   *
+   * // or with options
+   * bench.run({ 'async': true });
+   */
+  function run(options) {
+    var me = this,
+        event = Event('start');
+
+    // set `running` to `false` so `reset()` won't call `abort()`
+    me.running = false;
+    me.reset();
+    me.running = true;
+
+    me.count = me.initCount;
+    me.times.timeStamp = +new Date;
+    me.emit(event);
+
+    if (!event.cancelled) {
+      options = { 'async': ((options = options && options.async) == null ? me.async : options) && support.timeout };
+
+      // for clones created within `compute()`
+      if (me._original) {
+        if (me.defer) {
+          Deferred(me);
+        } else {
+          cycle(me, options);
+        }
+      }
+      // for original benchmarks
+      else {
+        compute(me, options);
+      }
+    }
+    return me;
+  }
+
+  /*--------------------------------------------------------------------------*/
+
+  // Firefox 1 erroneously defines variable and argument names of functions on
+  // the function itself as non-configurable properties with `undefined` values.
+  // The bugginess continues as the `Benchmark` constructor has an argument
+  // named `options` and Firefox 1 will not assign a value to `Benchmark.options`,
+  // making it non-writable in the process, unless it is the first property
+  // assigned by for-in loop of `extend()`.
+  extend(Benchmark, {
+
+    /**
+     * The default options copied by benchmark instances.
+     *
+     * @static
+     * @memberOf Benchmark
+     * @type Object
+     */
+    'options': {
+
+      /**
+       * A flag to indicate that benchmark cycles will execute asynchronously
+       * by default.
+       *
+       * @memberOf Benchmark.options
+       * @type Boolean
+       */
+      'async': false,
+
+      /**
+       * A flag to indicate that the benchmark clock is deferred.
+       *
+       * @memberOf Benchmark.options
+       * @type Boolean
+       */
+      'defer': false,
+
+      /**
+       * The delay between test cycles (secs).
+       * @memberOf Benchmark.options
+       * @type Number
+       */
+      'delay': 0.005,
+
+      /**
+       * Displayed by Benchmark#toString when a `name` is not available
+       * (auto-generated if absent).
+       *
+       * @memberOf Benchmark.options
+       * @type String
+       */
+      'id': undefined,
+
+      /**
+       * The default number of times to execute a test on a benchmark's first cycle.
+       *
+       * @memberOf Benchmark.options
+       * @type Number
+       */
+      'initCount': 1,
+
+      /**
+       * The maximum time a benchmark is allowed to run before finishing (secs).
+       * Note: Cycle delays aren't counted toward the maximum time.
+       *
+       * @memberOf Benchmark.options
+       * @type Number
+       */
+      'maxTime': 5,
+
+      /**
+       * The minimum sample size required to perform statistical analysis.
+       *
+       * @memberOf Benchmark.options
+       * @type Number
+       */
+      'minSamples': 5,
+
+      /**
+       * The time needed to reduce the percent uncertainty of measurement to 1% (secs).
+       *
+       * @memberOf Benchmark.options
+       * @type Number
+       */
+      'minTime': 0,
+
+      /**
+       * The name of the benchmark.
+       *
+       * @memberOf Benchmark.options
+       * @type String
+       */
+      'name': undefined,
+
+      /**
+       * An event listener called when the benchmark is aborted.
+       *
+       * @memberOf Benchmark.options
+       * @type Function
+       */
+      'onAbort': undefined,
+
+      /**
+       * An event listener called when the benchmark completes running.
+       *
+       * @memberOf Benchmark.options
+       * @type Function
+       */
+      'onComplete': undefined,
+
+      /**
+       * An event listener called after each run cycle.
+       *
+       * @memberOf Benchmark.options
+       * @type Function
+       */
+      'onCycle': undefined,
+
+      /**
+       * An event listener called when a test errors.
+       *
+       * @memberOf Benchmark.options
+       * @type Function
+       */
+      'onError': undefined,
+
+      /**
+       * An event listener called when the benchmark is reset.
+       *
+       * @memberOf Benchmark.options
+       * @type Function
+       */
+      'onReset': undefined,
+
+      /**
+       * An event listener called when the benchmark starts running.
+       *
+       * @memberOf Benchmark.options
+       * @type Function
+       */
+      'onStart': undefined
+    },
+
+    /**
+     * Platform object with properties describing things like browser name,
+     * version, and operating system.
+     *
+     * @static
+     * @memberOf Benchmark
+     * @type Object
+     */
+    'platform': req('platform') || window.platform || {
+
+      /**
+       * The platform description.
+       *
+       * @memberOf Benchmark.platform
+       * @type String
+       */
+      'description': window.navigator && navigator.userAgent || null,
+
+      /**
+       * The name of the browser layout engine.
+       *
+       * @memberOf Benchmark.platform
+       * @type String|Null
+       */
+      'layout': null,
+
+      /**
+       * The name of the product hosting the browser.
+       *
+       * @memberOf Benchmark.platform
+       * @type String|Null
+       */
+      'product': null,
+
+      /**
+       * The name of the browser/environment.
+       *
+       * @memberOf Benchmark.platform
+       * @type String|Null
+       */
+      'name': null,
+
+      /**
+       * The name of the product's manufacturer.
+       *
+       * @memberOf Benchmark.platform
+       * @type String|Null
+       */
+      'manufacturer': null,
+
+      /**
+       * The name of the operating system.
+       *
+       * @memberOf Benchmark.platform
+       * @type String|Null
+       */
+      'os': null,
+
+      /**
+       * The alpha/beta release indicator.
+       *
+       * @memberOf Benchmark.platform
+       * @type String|Null
+       */
+      'prerelease': null,
+
+      /**
+       * The browser/environment version.
+       *
+       * @memberOf Benchmark.platform
+       * @type String|Null
+       */
+      'version': null,
+
+      /**
+       * Return platform description when the platform object is coerced to a string.
+       *
+       * @memberOf Benchmark.platform
+       * @type Function
+       * @returns {String} The platform description.
+       */
+      'toString': function() {
+        return this.description || '';
+      }
+    },
+
+    /**
+     * The semantic version number.
+     *
+     * @static
+     * @memberOf Benchmark
+     * @type String
+     */
+    'version': '1.0.0',
+
+    // an object of environment/feature detection flags
+    'support': support,
+
+    // clone objects
+    'deepClone': deepClone,
+
+    // iteration utility
+    'each': each,
+
+    // augment objects
+    'extend': extend,
+
+    // generic Array#filter
+    'filter': filter,
+
+    // generic Array#forEach
+    'forEach': forEach,
+
+    // generic own property iteration utility
+    'forOwn': forOwn,
+
+    // converts a number to a comma-separated string
+    'formatNumber': formatNumber,
+
+    // generic Object#hasOwnProperty
+    // (trigger hasKey's lazy define before assigning it to Benchmark)
+    'hasKey': (hasKey(Benchmark, ''), hasKey),
+
+    // generic Array#indexOf
+    'indexOf': indexOf,
+
+    // template utility
+    'interpolate': interpolate,
+
+    // invokes a method on each item in an array
+    'invoke': invoke,
+
+    // generic Array#join for arrays and objects
+    'join': join,
+
+    // generic Array#map
+    'map': map,
+
+    // retrieves a property value from each item in an array
+    'pluck': pluck,
+
+    // generic Array#reduce
+    'reduce': reduce
+  });
+
+  /*--------------------------------------------------------------------------*/
+
+  extend(Benchmark.prototype, {
+
+    /**
+     * The number of times a test was executed.
+     *
+     * @memberOf Benchmark
+     * @type Number
+     */
+    'count': 0,
+
+    /**
+     * The number of cycles performed while benchmarking.
+     *
+     * @memberOf Benchmark
+     * @type Number
+     */
+    'cycles': 0,
+
+    /**
+     * The number of executions per second.
+     *
+     * @memberOf Benchmark
+     * @type Number
+     */
+    'hz': 0,
+
+    /**
+     * The compiled test function.
+     *
+     * @memberOf Benchmark
+     * @type Function|String
+     */
+    'compiled': undefined,
+
+    /**
+     * The error object if the test failed.
+     *
+     * @memberOf Benchmark
+     * @type Object
+     */
+    'error': undefined,
+
+    /**
+     * The test to benchmark.
+     *
+     * @memberOf Benchmark
+     * @type Function|String
+     */
+    'fn': undefined,
+
+    /**
+     * A flag to indicate if the benchmark is aborted.
+     *
+     * @memberOf Benchmark
+     * @type Boolean
+     */
+    'aborted': false,
+
+    /**
+     * A flag to indicate if the benchmark is running.
+     *
+     * @memberOf Benchmark
+     * @type Boolean
+     */
+    'running': false,
+
+    /**
+     * Compiled into the test and executed immediately **before** the test loop.
+     *
+     * @memberOf Benchmark
+     * @type Function|String
+     * @example
+     *
+     * // basic usage
+     * var bench = Benchmark({
+     *   'setup': function() {
+     *     var c = this.count,
+     *         element = document.getElementById('container');
+     *     while (c--) {
+     *       element.appendChild(document.createElement('div'));
+     *     }
+     *   },
+     *   'fn': function() {
+     *     element.removeChild(element.lastChild);
+     *   }
+     * });
+     *
+     * // compiles to something like:
+     * var c = this.count,
+     *     element = document.getElementById('container');
+     * while (c--) {
+     *   element.appendChild(document.createElement('div'));
+     * }
+     * var start = new Date;
+     * while (count--) {
+     *   element.removeChild(element.lastChild);
+     * }
+     * var end = new Date - start;
+     *
+     * // or using strings
+     * var bench = Benchmark({
+     *   'setup': '\
+     *     var a = 0;\n\
+     *     (function() {\n\
+     *       (function() {\n\
+     *         (function() {',
+     *   'fn': 'a += 1;',
+     *   'teardown': '\
+     *          }())\n\
+     *        }())\n\
+     *      }())'
+     * });
+     *
+     * // compiles to something like:
+     * var a = 0;
+     * (function() {
+     *   (function() {
+     *     (function() {
+     *       var start = new Date;
+     *       while (count--) {
+     *         a += 1;
+     *       }
+     *       var end = new Date - start;
+     *     }())
+     *   }())
+     * }())
+     */
+    'setup': noop,
+
+    /**
+     * Compiled into the test and executed immediately **after** the test loop.
+     *
+     * @memberOf Benchmark
+     * @type Function|String
+     */
+    'teardown': noop,
+
+    /**
+     * An object of stats including mean, margin or error, and standard deviation.
+     *
+     * @memberOf Benchmark
+     * @type Object
+     */
+    'stats': {
+
+      /**
+       * The margin of error.
+       *
+       * @memberOf Benchmark#stats
+       * @type Number
+       */
+      'moe': 0,
+
+      /**
+       * The relative margin of error (expressed as a percentage of the mean).
+       *
+       * @memberOf Benchmark#stats
+       * @type Number
+       */
+      'rme': 0,
+
+      /**
+       * The standard error of the mean.
+       *
+       * @memberOf Benchmark#stats
+       * @type Number
+       */
+      'sem': 0,
+
+      /**
+       * The sample standard deviation.
+       *
+       * @memberOf Benchmark#stats
+       * @type Number
+       */
+      'deviation': 0,
+
+      /**
+       * The sample arithmetic mean.
+       *
+       * @memberOf Benchmark#stats
+       * @type Number
+       */
+      'mean': 0,
+
+      /**
+       * The array of sampled periods.
+       *
+       * @memberOf Benchmark#stats
+       * @type Array
+       */
+      'sample': [],
+
+      /**
+       * The sample variance.
+       *
+       * @memberOf Benchmark#stats
+       * @type Number
+       */
+      'variance': 0
+    },
+
+    /**
+     * An object of timing data including cycle, elapsed, period, start, and stop.
+     *
+     * @memberOf Benchmark
+     * @type Object
+     */
+    'times': {
+
+      /**
+       * The time taken to complete the last cycle (secs).
+       *
+       * @memberOf Benchmark#times
+       * @type Number
+       */
+      'cycle': 0,
+
+      /**
+       * The time taken to complete the benchmark (secs).
+       *
+       * @memberOf Benchmark#times
+       * @type Number
+       */
+      'elapsed': 0,
+
+      /**
+       * The time taken to execute the test once (secs).
+       *
+       * @memberOf Benchmark#times
+       * @type Number
+       */
+      'period': 0,
+
+      /**
+       * A timestamp of when the benchmark started (ms).
+       *
+       * @memberOf Benchmark#times
+       * @type Number
+       */
+      'timeStamp': 0
+    },
+
+    // aborts benchmark (does not record times)
+    'abort': abort,
+
+    // creates a new benchmark using the same test and options
+    'clone': clone,
+
+    // compares benchmark's hertz with another
+    'compare': compare,
+
+    // executes listeners
+    'emit': emit,
+
+    // get listeners
+    'listeners': listeners,
+
+    // unregister listeners
+    'off': off,
+
+    // register listeners
+    'on': on,
+
+    // reset benchmark properties
+    'reset': reset,
+
+    // runs the benchmark
+    'run': run,
+
+    // pretty print benchmark info
+    'toString': toStringBench
+  });
+
+  /*--------------------------------------------------------------------------*/
+
+  extend(Deferred.prototype, {
+
+    /**
+     * The deferred benchmark instance.
+     *
+     * @memberOf Benchmark.Deferred
+     * @type Object
+     */
+    'benchmark': null,
+
+    /**
+     * The number of deferred cycles performed while benchmarking.
+     *
+     * @memberOf Benchmark.Deferred
+     * @type Number
+     */
+    'cycles': 0,
+
+    /**
+     * The time taken to complete the deferred benchmark (secs).
+     *
+     * @memberOf Benchmark.Deferred
+     * @type Number
+     */
+    'elapsed': 0,
+
+    /**
+     * A timestamp of when the deferred benchmark started (ms).
+     *
+     * @memberOf Benchmark.Deferred
+     * @type Number
+     */
+    'timeStamp': 0,
+
+    // cycles/completes the deferred benchmark
+    'resolve': resolve
+  });
+
+  /*--------------------------------------------------------------------------*/
+
+  extend(Event.prototype, {
+
+    /**
+     * A flag to indicate if the emitters listener iteration is aborted.
+     *
+     * @memberOf Benchmark.Event
+     * @type Boolean
+     */
+    'aborted': false,
+
+    /**
+     * A flag to indicate if the default action is cancelled.
+     *
+     * @memberOf Benchmark.Event
+     * @type Boolean
+     */
+    'cancelled': false,
+
+    /**
+     * The object whose listeners are currently being processed.
+     *
+     * @memberOf Benchmark.Event
+     * @type Object
+     */
+    'currentTarget': undefined,
+
+    /**
+     * The return value of the last executed listener.
+     *
+     * @memberOf Benchmark.Event
+     * @type Mixed
+     */
+    'result': undefined,
+
+    /**
+     * The object to which the event was originally emitted.
+     *
+     * @memberOf Benchmark.Event
+     * @type Object
+     */
+    'target': undefined,
+
+    /**
+     * A timestamp of when the event was created (ms).
+     *
+     * @memberOf Benchmark.Event
+     * @type Number
+     */
+    'timeStamp': 0,
+
+    /**
+     * The event type.
+     *
+     * @memberOf Benchmark.Event
+     * @type String
+     */
+    'type': ''
+  });
+
+  /*--------------------------------------------------------------------------*/
+
+  /**
+   * The default options copied by suite instances.
+   *
+   * @static
+   * @memberOf Benchmark.Suite
+   * @type Object
+   */
+  Suite.options = {
+
+    /**
+     * The name of the suite.
+     *
+     * @memberOf Benchmark.Suite.options
+     * @type String
+     */
+    'name': undefined
+  };
+
+  /*--------------------------------------------------------------------------*/
+
+  extend(Suite.prototype, {
+
+    /**
+     * The number of benchmarks in the suite.
+     *
+     * @memberOf Benchmark.Suite
+     * @type Number
+     */
+    'length': 0,
+
+    /**
+     * A flag to indicate if the suite is aborted.
+     *
+     * @memberOf Benchmark.Suite
+     * @type Boolean
+     */
+    'aborted': false,
+
+    /**
+     * A flag to indicate if the suite is running.
+     *
+     * @memberOf Benchmark.Suite
+     * @type Boolean
+     */
+    'running': false,
+
+    /**
+     * An `Array#forEach` like method.
+     * Callbacks may terminate the loop by explicitly returning `false`.
+     *
+     * @memberOf Benchmark.Suite
+     * @param {Function} callback The function called per iteration.
+     * @returns {Object} The suite iterated over.
+     */
+    'forEach': methodize(forEach),
+
+    /**
+     * An `Array#indexOf` like method.
+     *
+     * @memberOf Benchmark.Suite
+     * @param {Mixed} value The value to search for.
+     * @returns {Number} The index of the matched value or `-1`.
+     */
+    'indexOf': methodize(indexOf),
+
+    /**
+     * Invokes a method on all benchmarks in the suite.
+     *
+     * @memberOf Benchmark.Suite
+     * @param {String|Object} name The name of the method to invoke OR options object.
+     * @param {Mixed} [arg1, arg2, ...] Arguments to invoke the method with.
+     * @returns {Array} A new array of values returned from each method invoked.
+     */
+    'invoke': methodize(invoke),
+
+    /**
+     * Converts the suite of benchmarks to a string.
+     *
+     * @memberOf Benchmark.Suite
+     * @param {String} [separator=','] A string to separate each element of the array.
+     * @returns {String} The string.
+     */
+    'join': [].join,
+
+    /**
+     * An `Array#map` like method.
+     *
+     * @memberOf Benchmark.Suite
+     * @param {Function} callback The function called per iteration.
+     * @returns {Array} A new array of values returned by the callback.
+     */
+    'map': methodize(map),
+
+    /**
+     * Retrieves the value of a specified property from all benchmarks in the suite.
+     *
+     * @memberOf Benchmark.Suite
+     * @param {String} property The property to pluck.
+     * @returns {Array} A new array of property values.
+     */
+    'pluck': methodize(pluck),
+
+    /**
+     * Removes the last benchmark from the suite and returns it.
+     *
+     * @memberOf Benchmark.Suite
+     * @returns {Mixed} The removed benchmark.
+     */
+    'pop': [].pop,
+
+    /**
+     * Appends benchmarks to the suite.
+     *
+     * @memberOf Benchmark.Suite
+     * @returns {Number} The suite's new length.
+     */
+    'push': [].push,
+
+    /**
+     * Sorts the benchmarks of the suite.
+     *
+     * @memberOf Benchmark.Suite
+     * @param {Function} [compareFn=null] A function that defines the sort order.
+     * @returns {Object} The sorted suite.
+     */
+    'sort': [].sort,
+
+    /**
+     * An `Array#reduce` like method.
+     *
+     * @memberOf Benchmark.Suite
+     * @param {Function} callback The function called per iteration.
+     * @param {Mixed} accumulator Initial value of the accumulator.
+     * @returns {Mixed} The accumulator.
+     */
+    'reduce': methodize(reduce),
+
+    // aborts all benchmarks in the suite
+    'abort': abortSuite,
+
+    // adds a benchmark to the suite
+    'add': add,
+
+    // creates a new suite with cloned benchmarks
+    'clone': cloneSuite,
+
+    // executes listeners of a specified type
+    'emit': emit,
+
+    // creates a new suite of filtered benchmarks
+    'filter': filterSuite,
+
+    // get listeners
+    'listeners': listeners,
+
+    // unregister listeners
+    'off': off,
+
+   // register listeners
+    'on': on,
+
+    // resets all benchmarks in the suite
+    'reset': resetSuite,
+
+    // runs all benchmarks in the suite
+    'run': runSuite,
+
+    // array methods
+    'concat': concat,
+
+    'reverse': reverse,
+
+    'shift': shift,
+
+    'slice': slice,
+
+    'splice': splice,
+
+    'unshift': unshift
+  });
+
+  /*--------------------------------------------------------------------------*/
+
+  // expose Deferred, Event and Suite
+  extend(Benchmark, {
+    'Deferred': Deferred,
+    'Event': Event,
+    'Suite': Suite
+  });
+
+  // expose Benchmark
+  // some AMD build optimizers, like r.js, check for specific condition patterns like the following:
+  if (typeof define == 'function' && typeof define.amd == 'object' && define.amd) {
+    // define as an anonymous module so, through path mapping, it can be aliased
+    define(function() {
+      return Benchmark;
+    });
+  }
+  // check for `exports` after `define` in case a build optimizer adds an `exports` object
+  else if (freeExports) {
+    // in Node.js or RingoJS v0.8.0+
+    if (typeof module == 'object' && module && module.exports == freeExports) {
+      (module.exports = Benchmark).Benchmark = Benchmark;
+    }
+    // in Narwhal or RingoJS v0.7.0-
+    else {
+      freeExports.Benchmark = Benchmark;
+    }
+  }
+  // in a browser or Rhino
+  else {
+    // use square bracket notation so Closure Compiler won't munge `Benchmark`
+    // http://code.google.com/closure/compiler/docs/api-tutorial3.html#export
+    window['Benchmark'] = Benchmark;
+  }
+
+  // trigger clock's lazy define early to avoid a security error
+  if (support.air) {
+    clock({ '_original': { 'fn': noop, 'count': 1, 'options': {} } });
+  }
+}(this));
diff --git a/cep/node_modules/benchmark/doc/README.md b/cep/node_modules/benchmark/doc/README.md
new file mode 100644
index 0000000..5b8960a
--- /dev/null
+++ b/cep/node_modules/benchmark/doc/README.md
@@ -0,0 +1,2629 @@
+# Benchmark.js v1.0.0
+
+
+
+
+
+
+## `Benchmark`
+* [`Benchmark`](#benchmarkname-fn--options)
+* [`Benchmark.version`](#benchmarkversion)
+* [`Benchmark.deepClone`](#benchmarkdeepclonevalue)
+* [`Benchmark.each`](#benchmarkeachobject-callback-thisarg)
+* [`Benchmark.extend`](#benchmarkextenddestination--source)
+* [`Benchmark.filter`](#benchmarkfilterarray-callback-thisarg)
+* [`Benchmark.forEach`](#benchmarkforeacharray-callback-thisarg)
+* [`Benchmark.formatNumber`](#benchmarkformatnumbernumber)
+* [`Benchmark.forOwn`](#benchmarkforownobject-callback-thisarg)
+* [`Benchmark.hasKey`](#benchmarkhaskeyobject-key)
+* [`Benchmark.indexOf`](#benchmarkindexofarray-value--fromindex0)
+* [`Benchmark.interpolate`](#benchmarkinterpolatestring-object)
+* [`Benchmark.invoke`](#benchmarkinvokebenches-name--arg1-arg2-)
+* [`Benchmark.join`](#benchmarkjoinobject--separator1--separator2:)
+* [`Benchmark.map`](#benchmarkmaparray-callback-thisarg)
+* [`Benchmark.pluck`](#benchmarkpluckarray-property)
+* [`Benchmark.reduce`](#benchmarkreducearray-callback-accumulator)
+
+
+
+
+
+
+## `Benchmark.prototype`
+* [`Benchmark.prototype.aborted`](#benchmarkprototypeaborted)
+* [`Benchmark.prototype.compiled`](#benchmarkprototypecompiled)
+* [`Benchmark.prototype.count`](#benchmarkprototypecount)
+* [`Benchmark.prototype.cycles`](#benchmarkprototypecycles)
+* [`Benchmark.prototype.fn`](#benchmarkprototypefn)
+* [`Benchmark.prototype.hz`](#benchmarkprototypehz)
+* [`Benchmark.prototype.running`](#benchmarkprototyperunning)
+* [`Benchmark.prototype.setup`](#benchmarkprototypesetup)
+* [`Benchmark.prototype.teardown`](#benchmarkprototypeteardown)
+* [`Benchmark.prototype.abort`](#benchmarkprototypeabort)
+* [`Benchmark.prototype.clone`](#benchmarkprototypecloneoptions)
+* [`Benchmark.prototype.compare`](#benchmarkprototypecompareother)
+* [`Benchmark.prototype.emit`](#benchmarkprototypeemittype)
+* [`Benchmark.prototype.listeners`](#benchmarkprototypelistenerstype)
+* [`Benchmark.prototype.off`](#benchmarkprototypeofftype-listener)
+* [`Benchmark.prototype.on`](#benchmarkprototypeontype-listener)
+* [`Benchmark.prototype.reset`](#benchmarkprototypereset)
+* [`Benchmark.prototype.run`](#benchmarkprototyperunoptions)
+* [`Benchmark.prototype.toString`](#benchmarkprototypetostring)
+
+
+
+
+
+
+## `Benchmark.options`
+* [`Benchmark.options`](#benchmarkoptions)
+* [`Benchmark.options.async`](#benchmarkoptionsasync)
+* [`Benchmark.options.defer`](#benchmarkoptionsdefer)
+* [`Benchmark.options.delay`](#benchmarkoptionsdelay)
+* [`Benchmark.options.id`](#benchmarkoptionsid)
+* [`Benchmark.options.initCount`](#benchmarkoptionsinitcount)
+* [`Benchmark.options.maxTime`](#benchmarkoptionsmaxtime)
+* [`Benchmark.options.minSamples`](#benchmarkoptionsminsamples)
+* [`Benchmark.options.minTime`](#benchmarkoptionsmintime)
+* [`Benchmark.options.name`](#benchmarkoptionsname)
+* [`Benchmark.options.onAbort`](#benchmarkoptionsonabort)
+* [`Benchmark.options.onComplete`](#benchmarkoptionsoncomplete)
+* [`Benchmark.options.onCycle`](#benchmarkoptionsoncycle)
+* [`Benchmark.options.onError`](#benchmarkoptionsonerror)
+* [`Benchmark.options.onReset`](#benchmarkoptionsonreset)
+* [`Benchmark.options.onStart`](#benchmarkoptionsonstart)
+
+
+
+
+
+
+## `Benchmark.platform`
+* [`Benchmark.platform`](#benchmarkplatform)
+* [`Benchmark.platform.description`](#benchmarkplatformdescription)
+* [`Benchmark.platform.layout`](#benchmarkplatformlayout)
+* [`Benchmark.platform.manufacturer`](#benchmarkplatformmanufacturer)
+* [`Benchmark.platform.name`](#benchmarkplatformname)
+* [`Benchmark.platform.os`](#benchmarkplatformos)
+* [`Benchmark.platform.prerelease`](#benchmarkplatformprerelease)
+* [`Benchmark.platform.product`](#benchmarkplatformproduct)
+* [`Benchmark.platform.version`](#benchmarkplatformversion)
+* [`Benchmark.platform.toString`](#benchmarkplatformtostring)
+
+
+
+
+
+
+## `Benchmark.support`
+* [`Benchmark.support`](#benchmarksupport)
+* [`Benchmark.support.air`](#benchmarksupportair)
+* [`Benchmark.support.argumentsClass`](#benchmarksupportargumentsclass)
+* [`Benchmark.support.browser`](#benchmarksupportbrowser)
+* [`Benchmark.support.charByIndex`](#benchmarksupportcharbyindex)
+* [`Benchmark.support.charByOwnIndex`](#benchmarksupportcharbyownindex)
+* [`Benchmark.support.decompilation`](#benchmarksupportdecompilation)
+* [`Benchmark.support.descriptors`](#benchmarksupportdescriptors)
+* [`Benchmark.support.getAllKeys`](#benchmarksupportgetallkeys)
+* [`Benchmark.support.iteratesOwnLast`](#benchmarksupportiteratesownfirst)
+* [`Benchmark.support.java`](#benchmarksupportjava)
+* [`Benchmark.support.nodeClass`](#benchmarksupportnodeclass)
+* [`Benchmark.support.timeout`](#benchmarksupporttimeout)
+
+
+
+
+
+
+## `Benchmark.prototype.error`
+* [`Benchmark.prototype.error`](#benchmarkprototypeerror)
+
+
+
+
+
+
+## `Benchmark.prototype.stats`
+* [`Benchmark.prototype.stats`](#benchmarkprototypestats)
+* [`Benchmark.prototype.stats.deviation`](#benchmark-statsdeviation)
+* [`Benchmark.prototype.stats.mean`](#benchmark-statsmean)
+* [`Benchmark.prototype.stats.moe`](#benchmark-statsmoe)
+* [`Benchmark.prototype.stats.rme`](#benchmark-statsrme)
+* [`Benchmark.prototype.stats.sample`](#benchmark-statssample)
+* [`Benchmark.prototype.stats.sem`](#benchmark-statssem)
+* [`Benchmark.prototype.stats.variance`](#benchmark-statsvariance)
+
+
+
+
+
+
+## `Benchmark.prototype.times`
+* [`Benchmark.prototype.times`](#benchmarkprototypetimes)
+* [`Benchmark.prototype.times.cycle`](#benchmark-timescycle)
+* [`Benchmark.prototype.times.elapsed`](#benchmark-timeselapsed)
+* [`Benchmark.prototype.times.period`](#benchmark-timesperiod)
+* [`Benchmark.prototype.times.timeStamp`](#benchmark-timestimestamp)
+
+
+
+
+
+
+## `Benchmark.Deferred`
+* [`Benchmark.Deferred`](#benchmarkdeferredclone)
+
+
+
+
+
+
+## `Benchmark.Deferred.prototype`
+* [`Benchmark.Deferred.prototype.benchmark`](#benchmarkdeferredprototypebenchmark)
+* [`Benchmark.Deferred.prototype.cycles`](#benchmarkdeferredprototypecycles)
+* [`Benchmark.Deferred.prototype.elapsed`](#benchmarkdeferredprototypeelapsed)
+* [`Benchmark.Deferred.prototype.resolve`](#benchmarkdeferredprototyperesolve)
+* [`Benchmark.Deferred.prototype.timeStamp`](#benchmarkdeferredprototypetimestamp)
+
+
+
+
+
+
+## `Benchmark.Event`
+* [`Benchmark.Event`](#benchmarkeventtype)
+
+
+
+
+
+
+## `Benchmark.Event.prototype`
+* [`Benchmark.Event.prototype.aborted`](#benchmarkeventprototypeaborted)
+* [`Benchmark.Event.prototype.cancelled`](#benchmarkeventprototypecancelled)
+* [`Benchmark.Event.prototype.result`](#benchmarkeventprototyperesult)
+* [`Benchmark.Event.prototype.timeStamp`](#benchmarkeventprototypetimestamp)
+* [`Benchmark.Event.prototype.type`](#benchmarkeventprototypetype)
+
+
+
+
+
+
+## `Benchmark.Event.prototype.currentTarget`
+* [`Benchmark.Event.prototype.currentTarget`](#benchmarkeventprototypecurrenttarget)
+
+
+
+
+
+
+## `Benchmark.Event.prototype.target`
+* [`Benchmark.Event.prototype.target`](#benchmarkeventprototypetarget)
+
+
+
+
+
+
+## `Benchmark.Suite`
+* [`Benchmark.Suite`](#benchmarksuitename--options)
+
+
+
+
+
+
+## `Benchmark.Suite.prototype`
+* [`Benchmark.Suite.prototype.aborted`](#benchmarksuiteprototypeaborted)
+* [`Benchmark.Suite.prototype.length`](#benchmarksuiteprototypelength)
+* [`Benchmark.Suite.prototype.running`](#benchmarksuiteprototyperunning)
+* [`Benchmark.Suite.prototype.abort`](#benchmarksuiteprototypeabort)
+* [`Benchmark.Suite.prototype.add`](#benchmarksuiteprototypeaddname-fn--options)
+* [`Benchmark.Suite.prototype.clone`](#benchmarksuiteprototypecloneoptions)
+* [`Benchmark.Suite.prototype.emit`](#benchmarkprototypeemittype)
+* [`Benchmark.Suite.prototype.filter`](#benchmarksuiteprototypefiltercallback)
+* [`Benchmark.Suite.prototype.forEach`](#benchmarksuiteprototypeforeachcallback)
+* [`Benchmark.Suite.prototype.indexOf`](#benchmarksuiteprototypeindexofvalue)
+* [`Benchmark.Suite.prototype.invoke`](#benchmarksuiteprototypeinvokename--arg1-arg2-)
+* [`Benchmark.Suite.prototype.join`](#benchmarksuiteprototypejoinseparator-)
+* [`Benchmark.Suite.prototype.listeners`](#benchmarkprototypelistenerstype)
+* [`Benchmark.Suite.prototype.map`](#benchmarksuiteprototypemapcallback)
+* [`Benchmark.Suite.prototype.off`](#benchmarkprototypeofftype-listener)
+* [`Benchmark.Suite.prototype.on`](#benchmarkprototypeontype-listener)
+* [`Benchmark.Suite.prototype.pluck`](#benchmarksuiteprototypepluckproperty)
+* [`Benchmark.Suite.prototype.pop`](#benchmarksuiteprototypepop)
+* [`Benchmark.Suite.prototype.push`](#benchmarksuiteprototypepush)
+* [`Benchmark.Suite.prototype.reduce`](#benchmarksuiteprototypereducecallback-accumulator)
+* [`Benchmark.Suite.prototype.reset`](#benchmarksuiteprototypereset)
+* [`Benchmark.Suite.prototype.reverse`](#benchmarksuiteprototypereverse)
+* [`Benchmark.Suite.prototype.run`](#benchmarksuiteprototyperunoptions)
+* [`Benchmark.Suite.prototype.shift`](#benchmarksuiteprototypeshift)
+* [`Benchmark.Suite.prototype.slice`](#benchmarksuiteprototypeslicestart-end)
+* [`Benchmark.Suite.prototype.sort`](#benchmarksuiteprototypesortcomparefnnull)
+* [`Benchmark.Suite.prototype.splice`](#benchmarksuiteprototypesplicestart-deletecount--val1-val2-)
+* [`Benchmark.Suite.prototype.unshift`](#benchmarksuiteprototypeunshift)
+
+
+
+
+
+
+## `Benchmark.Suite.options`
+* [`Benchmark.Suite.options`](#benchmarksuiteoptions)
+* [`Benchmark.Suite.options.name`](#benchmarksuiteoptionsname)
+
+
+
+
+
+
+
+
+
+
+
+
+## `Benchmark`
+
+
+
+### `Benchmark(name, fn [, options={}])`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L404 "View in source") [Ⓣ][1]
+
+The Benchmark constructor.
+
+#### Arguments
+1. `name` *(String)*: A name to identify the benchmark.
+2. `fn` *(Function|String)*: The test to benchmark.
+3. `[options={}]` *(Object)*: Options object.
+
+#### Example
+~~~ js
+// basic usage (the `new` operator is optional)
+var bench = new Benchmark(fn);
+
+// or using a name first
+var bench = new Benchmark('foo', fn);
+
+// or with options
+var bench = new Benchmark('foo', fn, {
+
+  // displayed by Benchmark#toString if `name` is not available
+  'id': 'xyz',
+
+  // called when the benchmark starts running
+  'onStart': onStart,
+
+  // called after each run cycle
+  'onCycle': onCycle,
+
+  // called when aborted
+  'onAbort': onAbort,
+
+  // called when a test errors
+  'onError': onError,
+
+  // called when reset
+  'onReset': onReset,
+
+  // called when the benchmark completes running
+  'onComplete': onComplete,
+
+  // compiled/called before the test loop
+  'setup': setup,
+
+  // compiled/called after the test loop
+  'teardown': teardown
+});
+
+// or name and options
+var bench = new Benchmark('foo', {
+
+  // a flag to indicate the benchmark is deferred
+  'defer': true,
+
+  // benchmark test function
+  'fn': function(deferred) {
+    // call resolve() when the deferred test is finished
+    deferred.resolve();
+  }
+});
+
+// or options only
+var bench = new Benchmark({
+
+  // benchmark name
+  'name': 'foo',
+
+  // benchmark test as a string
+  'fn': '[1,2,3,4].sort()'
+});
+
+// a test's `this` binding is set to the benchmark instance
+var bench = new Benchmark('foo', function() {
+  'My name is '.concat(this.name); // My name is foo
+});
+~~~
+
+* * *
+
+
+
+
+
+
+### `Benchmark.version`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3267 "View in source") [Ⓣ][1]
+
+*(String)*: The semantic version number.
+
+* * *
+
+
+
+
+
+
+### `Benchmark.deepClone(value)`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L1225 "View in source") [Ⓣ][1]
+
+A deep clone utility.
+
+#### Arguments
+1. `value` *(Mixed)*: The value to clone.
+
+#### Returns
+*(Mixed)*: The cloned value.
+
+* * *
+
+
+
+
+
+
+### `Benchmark.each(object, callback, thisArg)`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L1400 "View in source") [Ⓣ][1]
+
+An iteration utility for arrays and objects. Callbacks may terminate the loop by explicitly returning `false`.
+
+#### Arguments
+1. `object` *(Array|Object)*: The object to iterate over.
+2. `callback` *(Function)*: The function called per iteration.
+3. `thisArg` *(Mixed)*: The `this` binding for the callback.
+
+#### Returns
+*(Array, Object)*: Returns the object iterated over.
+
+* * *
+
+
+
+
+
+
+### `Benchmark.extend(destination [, source={}])`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L1446 "View in source") [Ⓣ][1]
+
+Copies enumerable properties from the source(s) object to the destination object.
+
+#### Arguments
+1. `destination` *(Object)*: The destination object.
+2. `[source={}]` *(Object)*: The source object.
+
+#### Returns
+*(Object)*: The destination object.
+
+* * *
+
+
+
+
+
+
+### `Benchmark.filter(array, callback, thisArg)`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L1485 "View in source") [Ⓣ][1]
+
+A generic `Array#filter` like method.
+
+#### Arguments
+1. `array` *(Array)*: The array to iterate over.
+2. `callback` *(Function|String)*: The function/alias called per iteration.
+3. `thisArg` *(Mixed)*: The `this` binding for the callback.
+
+#### Returns
+*(Array)*: A new array of values that passed callback filter.
+
+#### Example
+~~~ js
+// get odd numbers
+Benchmark.filter([1, 2, 3, 4, 5], function(n) {
+  return n % 2;
+}); // -> [1, 3, 5];
+
+// get fastest benchmarks
+Benchmark.filter(benches, 'fastest');
+
+// get slowest benchmarks
+Benchmark.filter(benches, 'slowest');
+
+// get benchmarks that completed without erroring
+Benchmark.filter(benches, 'successful');
+~~~
+
+* * *
+
+
+
+
+
+
+### `Benchmark.forEach(array, callback, thisArg)`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L1518 "View in source") [Ⓣ][1]
+
+A generic `Array#forEach` like method. Callbacks may terminate the loop by explicitly returning `false`.
+
+#### Arguments
+1. `array` *(Array)*: The array to iterate over.
+2. `callback` *(Function)*: The function called per iteration.
+3. `thisArg` *(Mixed)*: The `this` binding for the callback.
+
+#### Returns
+*(Array)*: Returns the array iterated over.
+
+* * *
+
+
+
+
+
+
+### `Benchmark.formatNumber(number)`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L1557 "View in source") [Ⓣ][1]
+
+Converts a number to a more readable comma-separated string representation.
+
+#### Arguments
+1. `number` *(Number)*: The number to convert.
+
+#### Returns
+*(String)*: The more readable string representation.
+
+* * *
+
+
+
+
+
+
+### `Benchmark.forOwn(object, callback, thisArg)`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L1545 "View in source") [Ⓣ][1]
+
+Iterates over an object's own properties, executing the `callback` for each. Callbacks may terminate the loop by explicitly returning `false`.
+
+#### Arguments
+1. `object` *(Object)*: The object to iterate over.
+2. `callback` *(Function)*: The function executed per own property.
+3. `thisArg` *(Mixed)*: The `this` binding for the callback.
+
+#### Returns
+*(Object)*: Returns the object iterated over.
+
+* * *
+
+
+
+
+
+
+### `Benchmark.hasKey(object, key)`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L1572 "View in source") [Ⓣ][1]
+
+Checks if an object has the specified key as a direct property.
+
+#### Arguments
+1. `object` *(Object)*: The object to check.
+2. `key` *(String)*: The key to check for.
+
+#### Returns
+*(Boolean)*: Returns `true` if key is a direct property, else `false`.
+
+* * *
+
+
+
+
+
+
+### `Benchmark.indexOf(array, value [, fromIndex=0])`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L1608 "View in source") [Ⓣ][1]
+
+A generic `Array#indexOf` like method.
+
+#### Arguments
+1. `array` *(Array)*: The array to iterate over.
+2. `value` *(Mixed)*: The value to search for.
+3. `[fromIndex=0]` *(Number)*: The index to start searching from.
+
+#### Returns
+*(Number)*: The index of the matched value or `-1`.
+
+* * *
+
+
+
+
+
+
+### `Benchmark.interpolate(string, object)`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L1630 "View in source") [Ⓣ][1]
+
+Modify a string by replacing named tokens with matching object property values.
+
+#### Arguments
+1. `string` *(String)*: The string to modify.
+2. `object` *(Object)*: The template object.
+
+#### Returns
+*(String)*: The modified string.
+
+* * *
+
+
+
+
+
+
+### `Benchmark.invoke(benches, name [, arg1, arg2, ...])`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L1677 "View in source") [Ⓣ][1]
+
+Invokes a method on all items in an array.
+
+#### Arguments
+1. `benches` *(Array)*: Array of benchmarks to iterate over.
+2. `name` *(String|Object)*: The name of the method to invoke OR options object.
+3. `[arg1, arg2, ...]` *(Mixed)*: Arguments to invoke the method with.
+
+#### Returns
+*(Array)*: A new array of values returned from each method invoked.
+
+#### Example
+~~~ js
+// invoke `reset` on all benchmarks
+Benchmark.invoke(benches, 'reset');
+
+// invoke `emit` with arguments
+Benchmark.invoke(benches, 'emit', 'complete', listener);
+
+// invoke `run(true)`, treat benchmarks as a queue, and register invoke callbacks
+Benchmark.invoke(benches, {
+
+  // invoke the `run` method
+  'name': 'run',
+
+  // pass a single argument
+  'args': true,
+
+  // treat as queue, removing benchmarks from front of `benches` until empty
+  'queued': true,
+
+  // called before any benchmarks have been invoked.
+  'onStart': onStart,
+
+  // called between invoking benchmarks
+  'onCycle': onCycle,
+
+  // called after all benchmarks have been invoked.
+  'onComplete': onComplete
+});
+~~~
+
+* * *
+
+
+
+
+
+
+### `Benchmark.join(object [, separator1=',', separator2=': '])`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L1831 "View in source") [Ⓣ][1]
+
+Creates a string of joined array values or object key-value pairs.
+
+#### Arguments
+1. `object` *(Array|Object)*: The object to operate on.
+2. `[separator1=',']` *(String)*: The separator used between key-value pairs.
+3. `[separator2=': ']` *(String)*: The separator used between keys and values.
+
+#### Returns
+*(String)*: The joined result.
+
+* * *
+
+
+
+
+
+
+### `Benchmark.map(array, callback, thisArg)`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L1853 "View in source") [Ⓣ][1]
+
+A generic `Array#map` like method.
+
+#### Arguments
+1. `array` *(Array)*: The array to iterate over.
+2. `callback` *(Function)*: The function called per iteration.
+3. `thisArg` *(Mixed)*: The `this` binding for the callback.
+
+#### Returns
+*(Array)*: A new array of values returned by the callback.
+
+* * *
+
+
+
+
+
+
+### `Benchmark.pluck(array, property)`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L1869 "View in source") [Ⓣ][1]
+
+Retrieves the value of a specified property from all items in an array.
+
+#### Arguments
+1. `array` *(Array)*: The array to iterate over.
+2. `property` *(String)*: The property to pluck.
+
+#### Returns
+*(Array)*: A new array of property values.
+
+* * *
+
+
+
+
+
+
+### `Benchmark.reduce(array, callback, accumulator)`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L1885 "View in source") [Ⓣ][1]
+
+A generic `Array#reduce` like method.
+
+#### Arguments
+1. `array` *(Array)*: The array to iterate over.
+2. `callback` *(Function)*: The function called per iteration.
+3. `accumulator` *(Mixed)*: Initial value of the accumulator.
+
+#### Returns
+*(Mixed)*: The accumulator.
+
+* * *
+
+
+
+
+
+
+
+
+
+## `Benchmark.prototype`
+
+
+
+### `Benchmark.prototype.aborted`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3377 "View in source") [Ⓣ][1]
+
+*(Boolean)*: A flag to indicate if the benchmark is aborted.
+
+* * *
+
+
+
+
+
+
+### `Benchmark.prototype.compiled`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3353 "View in source") [Ⓣ][1]
+
+*(Function, String)*: The compiled test function.
+
+* * *
+
+
+
+
+
+
+### `Benchmark.prototype.count`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3329 "View in source") [Ⓣ][1]
+
+*(Number)*: The number of times a test was executed.
+
+* * *
+
+
+
+
+
+
+### `Benchmark.prototype.cycles`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3337 "View in source") [Ⓣ][1]
+
+*(Number)*: The number of cycles performed while benchmarking.
+
+* * *
+
+
+
+
+
+
+### `Benchmark.prototype.fn`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3369 "View in source") [Ⓣ][1]
+
+*(Function, String)*: The test to benchmark.
+
+* * *
+
+
+
+
+
+
+### `Benchmark.prototype.hz`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3345 "View in source") [Ⓣ][1]
+
+*(Number)*: The number of executions per second.
+
+* * *
+
+
+
+
+
+
+### `Benchmark.prototype.running`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3385 "View in source") [Ⓣ][1]
+
+*(Boolean)*: A flag to indicate if the benchmark is running.
+
+* * *
+
+
+
+
+
+
+### `Benchmark.prototype.setup`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3448 "View in source") [Ⓣ][1]
+
+*(Function, String)*: Compiled into the test and executed immediately **before** the test loop.
+
+#### Example
+~~~ js
+// basic usage
+var bench = Benchmark({
+  'setup': function() {
+    var c = this.count,
+        element = document.getElementById('container');
+    while (c--) {
+      element.appendChild(document.createElement('div'));
+    }
+  },
+  'fn': function() {
+    element.removeChild(element.lastChild);
+  }
+});
+
+// compiles to something like:
+var c = this.count,
+    element = document.getElementById('container');
+while (c--) {
+  element.appendChild(document.createElement('div'));
+}
+var start = new Date;
+while (count--) {
+  element.removeChild(element.lastChild);
+}
+var end = new Date - start;
+
+// or using strings
+var bench = Benchmark({
+  'setup': '\
+    var a = 0;\n\
+    (function() {\n\
+      (function() {\n\
+        (function() {',
+  'fn': 'a += 1;',
+  'teardown': '\
+         }())\n\
+       }())\n\
+     }())'
+});
+
+// compiles to something like:
+var a = 0;
+(function() {
+  (function() {
+    (function() {
+      var start = new Date;
+      while (count--) {
+        a += 1;
+      }
+      var end = new Date - start;
+    }())
+  }())
+}())
+~~~
+
+* * *
+
+
+
+
+
+
+### `Benchmark.prototype.teardown`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3456 "View in source") [Ⓣ][1]
+
+*(Function, String)*: Compiled into the test and executed immediately **after** the test loop.
+
+* * *
+
+
+
+
+
+
+### `Benchmark.prototype.abort()`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L2218 "View in source") [Ⓣ][1]
+
+Aborts the benchmark without recording times.
+
+#### Returns
+*(Object)*: The benchmark instance.
+
+* * *
+
+
+
+
+
+
+### `Benchmark.prototype.clone(options)`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L2257 "View in source") [Ⓣ][1]
+
+Creates a new benchmark using the same test and options.
+
+#### Arguments
+1. `options` *(Object)*: Options object to overwrite cloned options.
+
+#### Returns
+*(Object)*: The new benchmark instance.
+
+#### Example
+~~~ js
+var bizarro = bench.clone({
+  'name': 'doppelganger'
+});
+~~~
+
+* * *
+
+
+
+
+
+
+### `Benchmark.prototype.compare(other)`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L2280 "View in source") [Ⓣ][1]
+
+Determines if a benchmark is faster than another.
+
+#### Arguments
+1. `other` *(Object)*: The benchmark to compare.
+
+#### Returns
+*(Number)*: Returns `-1` if slower, `1` if faster, and `0` if indeterminate.
+
+* * *
+
+
+
+
+
+
+### `Benchmark.Suite.prototype.emit(type)`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L2095 "View in source") [Ⓣ][1]
+
+Executes all registered listeners of the specified event type.
+
+#### Arguments
+1. `type` *(String|Object)*: The event type or object.
+
+#### Returns
+*(Mixed)*: Returns the return value of the last listener executed.
+
+* * *
+
+
+
+
+
+
+### `Benchmark.Suite.prototype.listeners(type)`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L2125 "View in source") [Ⓣ][1]
+
+Returns an array of event listeners for a given type that can be manipulated to add or remove listeners.
+
+#### Arguments
+1. `type` *(String)*: The event type.
+
+#### Returns
+*(Array)*: The listeners array.
+
+* * *
+
+
+
+
+
+
+### `Benchmark.Suite.prototype.off([type, listener])`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L2158 "View in source") [Ⓣ][1]
+
+Unregisters a listener for the specified event type(s), or unregisters all listeners for the specified event type(s), or unregisters all listeners for all event types.
+
+#### Arguments
+1. `[type]` *(String)*: The event type.
+2. `[listener]` *(Function)*: The function to unregister.
+
+#### Returns
+*(Object)*: The benchmark instance.
+
+#### Example
+~~~ js
+// unregister a listener for an event type
+bench.off('cycle', listener);
+
+// unregister a listener for multiple event types
+bench.off('start cycle', listener);
+
+// unregister all listeners for an event type
+bench.off('cycle');
+
+// unregister all listeners for multiple event types
+bench.off('start cycle complete');
+
+// unregister all listeners for all event types
+bench.off();
+~~~
+
+* * *
+
+
+
+
+
+
+### `Benchmark.Suite.prototype.on(type, listener)`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L2197 "View in source") [Ⓣ][1]
+
+Registers a listener for the specified event type(s).
+
+#### Arguments
+1. `type` *(String)*: The event type.
+2. `listener` *(Function)*: The function to register.
+
+#### Returns
+*(Object)*: The benchmark instance.
+
+#### Example
+~~~ js
+// register a listener for an event type
+bench.on('cycle', listener);
+
+// register a listener for multiple event types
+bench.on('start cycle', listener);
+~~~
+
+* * *
+
+
+
+
+
+
+### `Benchmark.prototype.reset()`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L2334 "View in source") [Ⓣ][1]
+
+Reset properties and abort if running.
+
+#### Returns
+*(Object)*: The benchmark instance.
+
+* * *
+
+
+
+
+
+
+### `Benchmark.prototype.run([options={}])`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3000 "View in source") [Ⓣ][1]
+
+Runs the benchmark.
+
+#### Arguments
+1. `[options={}]` *(Object)*: Options object.
+
+#### Returns
+*(Object)*: The benchmark instance.
+
+#### Example
+~~~ js
+// basic usage
+bench.run();
+
+// or with options
+bench.run({ 'async': true });
+~~~
+
+* * *
+
+
+
+
+
+
+### `Benchmark.prototype.toString()`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L2405 "View in source") [Ⓣ][1]
+
+Displays relevant benchmark information when coerced to a string.
+
+#### Returns
+*(String)*: A string representation of the benchmark instance.
+
+* * *
+
+
+
+
+
+
+
+
+
+## `Benchmark.options`
+
+
+
+### `Benchmark.options`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3049 "View in source") [Ⓣ][1]
+
+*(Object)*: The default options copied by benchmark instances.
+
+* * *
+
+
+
+
+
+
+### `Benchmark.options.async`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3058 "View in source") [Ⓣ][1]
+
+*(Boolean)*: A flag to indicate that benchmark cycles will execute asynchronously by default.
+
+* * *
+
+
+
+
+
+
+### `Benchmark.options.defer`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3066 "View in source") [Ⓣ][1]
+
+*(Boolean)*: A flag to indicate that the benchmark clock is deferred.
+
+* * *
+
+
+
+
+
+
+### `Benchmark.options.delay`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3073 "View in source") [Ⓣ][1]
+
+*(Number)*: The delay between test cycles *(secs)*.
+
+* * *
+
+
+
+
+
+
+### `Benchmark.options.id`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3082 "View in source") [Ⓣ][1]
+
+*(String)*: Displayed by Benchmark#toString when a `name` is not available *(auto-generated if absent)*.
+
+* * *
+
+
+
+
+
+
+### `Benchmark.options.initCount`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3090 "View in source") [Ⓣ][1]
+
+*(Number)*: The default number of times to execute a test on a benchmark's first cycle.
+
+* * *
+
+
+
+
+
+
+### `Benchmark.options.maxTime`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3099 "View in source") [Ⓣ][1]
+
+*(Number)*: The maximum time a benchmark is allowed to run before finishing *(secs)*. Note: Cycle delays aren't counted toward the maximum time.
+
+* * *
+
+
+
+
+
+
+### `Benchmark.options.minSamples`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3107 "View in source") [Ⓣ][1]
+
+*(Number)*: The minimum sample size required to perform statistical analysis.
+
+* * *
+
+
+
+
+
+
+### `Benchmark.options.minTime`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3115 "View in source") [Ⓣ][1]
+
+*(Number)*: The time needed to reduce the percent uncertainty of measurement to `1`% *(secs)*.
+
+* * *
+
+
+
+
+
+
+### `Benchmark.options.name`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3123 "View in source") [Ⓣ][1]
+
+*(String)*: The name of the benchmark.
+
+* * *
+
+
+
+
+
+
+### `Benchmark.options.onAbort`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3131 "View in source") [Ⓣ][1]
+
+An event listener called when the benchmark is aborted.
+
+* * *
+
+
+
+
+
+
+### `Benchmark.options.onComplete`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3139 "View in source") [Ⓣ][1]
+
+An event listener called when the benchmark completes running.
+
+* * *
+
+
+
+
+
+
+### `Benchmark.options.onCycle`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3147 "View in source") [Ⓣ][1]
+
+An event listener called after each run cycle.
+
+* * *
+
+
+
+
+
+
+### `Benchmark.options.onError`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3155 "View in source") [Ⓣ][1]
+
+An event listener called when a test errors.
+
+* * *
+
+
+
+
+
+
+### `Benchmark.options.onReset`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3163 "View in source") [Ⓣ][1]
+
+An event listener called when the benchmark is reset.
+
+* * *
+
+
+
+
+
+
+### `Benchmark.options.onStart`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3171 "View in source") [Ⓣ][1]
+
+An event listener called when the benchmark starts running.
+
+* * *
+
+
+
+
+
+
+
+
+
+## `Benchmark.platform`
+
+
+
+### `Benchmark.platform`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3182 "View in source") [Ⓣ][1]
+
+*(Object)*: Platform object with properties describing things like browser name, version, and operating system.
+
+* * *
+
+
+
+
+
+
+### `Benchmark.platform.description`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3190 "View in source") [Ⓣ][1]
+
+*(String)*: The platform description.
+
+* * *
+
+
+
+
+
+
+### `Benchmark.platform.layout`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3198 "View in source") [Ⓣ][1]
+
+*(String, Null)*: The name of the browser layout engine.
+
+* * *
+
+
+
+
+
+
+### `Benchmark.platform.manufacturer`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3222 "View in source") [Ⓣ][1]
+
+*(String, Null)*: The name of the product's manufacturer.
+
+* * *
+
+
+
+
+
+
+### `Benchmark.platform.name`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3214 "View in source") [Ⓣ][1]
+
+*(String, Null)*: The name of the browser/environment.
+
+* * *
+
+
+
+
+
+
+### `Benchmark.platform.os`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3230 "View in source") [Ⓣ][1]
+
+*(String, Null)*: The name of the operating system.
+
+* * *
+
+
+
+
+
+
+### `Benchmark.platform.prerelease`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3238 "View in source") [Ⓣ][1]
+
+*(String, Null)*: The alpha/beta release indicator.
+
+* * *
+
+
+
+
+
+
+### `Benchmark.platform.product`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3206 "View in source") [Ⓣ][1]
+
+*(String, Null)*: The name of the product hosting the browser.
+
+* * *
+
+
+
+
+
+
+### `Benchmark.platform.version`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3246 "View in source") [Ⓣ][1]
+
+*(String, Null)*: The browser/environment version.
+
+* * *
+
+
+
+
+
+
+### `Benchmark.platform.toString()`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3255 "View in source") [Ⓣ][1]
+
+Return platform description when the platform object is coerced to a string.
+
+#### Returns
+*(String)*: The platform description.
+
+* * *
+
+
+
+
+
+
+
+
+
+## `Benchmark.support`
+
+
+
+### `Benchmark.support`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L135 "View in source") [Ⓣ][1]
+
+*(Object)*: An object used to flag environments/features.
+
+* * *
+
+
+
+
+
+
+### `Benchmark.support.air`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L145 "View in source") [Ⓣ][1]
+
+*(Boolean)*: Detect Adobe AIR.
+
+* * *
+
+
+
+
+
+
+### `Benchmark.support.argumentsClass`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L153 "View in source") [Ⓣ][1]
+
+*(Boolean)*: Detect if `arguments` objects have the correct internal [[Class]] value.
+
+* * *
+
+
+
+
+
+
+### `Benchmark.support.browser`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L161 "View in source") [Ⓣ][1]
+
+*(Boolean)*: Detect if in a browser environment.
+
+* * *
+
+
+
+
+
+
+### `Benchmark.support.charByIndex`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L169 "View in source") [Ⓣ][1]
+
+*(Boolean)*: Detect if strings support accessing characters by index.
+
+* * *
+
+
+
+
+
+
+### `Benchmark.support.charByOwnIndex`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L179 "View in source") [Ⓣ][1]
+
+*(Boolean)*: Detect if strings have indexes as own properties.
+
+* * *
+
+
+
+
+
+
+### `Benchmark.support.decompilation`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L207 "View in source") [Ⓣ][1]
+
+*(Boolean)*: Detect if functions support decompilation.
+
+* * *
+
+
+
+
+
+
+### `Benchmark.support.descriptors`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L228 "View in source") [Ⓣ][1]
+
+*(Boolean)*: Detect ES5+ property descriptor API.
+
+* * *
+
+
+
+
+
+
+### `Benchmark.support.getAllKeys`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L242 "View in source") [Ⓣ][1]
+
+*(Boolean)*: Detect ES5+ Object.getOwnPropertyNames().
+
+* * *
+
+
+
+
+
+
+### `Benchmark.support.iteratesOwnFirst`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L255 "View in source") [Ⓣ][1]
+
+*(Boolean)*: Detect if own properties are iterated before inherited properties *(all but IE < `9`)*.
+
+* * *
+
+
+
+
+
+
+### `Benchmark.support.java`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L190 "View in source") [Ⓣ][1]
+
+*(Boolean)*: Detect if Java is enabled/exposed.
+
+* * *
+
+
+
+
+
+
+### `Benchmark.support.nodeClass`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L272 "View in source") [Ⓣ][1]
+
+*(Boolean)*: Detect if a node's [[Class]] is resolvable *(all but IE < `9`)* and that the JS engine errors when attempting to coerce an object to a string without a `toString` property value of `typeof` "function".
+
+* * *
+
+
+
+
+
+
+### `Benchmark.support.timeout`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L198 "View in source") [Ⓣ][1]
+
+*(Boolean)*: Detect if the Timers API exists.
+
+* * *
+
+
+
+
+
+
+
+
+
+## `Benchmark.prototype.error`
+
+
+
+### `Benchmark.prototype.error`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3361 "View in source") [Ⓣ][1]
+
+*(Object)*: The error object if the test failed.
+
+* * *
+
+
+
+
+
+
+
+
+
+## `Benchmark.prototype.stats`
+
+
+
+### `Benchmark.prototype.stats`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3464 "View in source") [Ⓣ][1]
+
+*(Object)*: An object of stats including mean, margin or error, and standard deviation.
+
+* * *
+
+
+
+
+
+
+### `Benchmark.prototype.stats.deviation`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3496 "View in source") [Ⓣ][1]
+
+*(Number)*: The sample standard deviation.
+
+* * *
+
+
+
+
+
+
+### `Benchmark.prototype.stats.mean`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3504 "View in source") [Ⓣ][1]
+
+*(Number)*: The sample arithmetic mean.
+
+* * *
+
+
+
+
+
+
+### `Benchmark.prototype.stats.moe`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3472 "View in source") [Ⓣ][1]
+
+*(Number)*: The margin of error.
+
+* * *
+
+
+
+
+
+
+### `Benchmark.prototype.stats.rme`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3480 "View in source") [Ⓣ][1]
+
+*(Number)*: The relative margin of error *(expressed as a percentage of the mean)*.
+
+* * *
+
+
+
+
+
+
+### `Benchmark.prototype.stats.sample`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3512 "View in source") [Ⓣ][1]
+
+*(Array)*: The array of sampled periods.
+
+* * *
+
+
+
+
+
+
+### `Benchmark.prototype.stats.sem`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3488 "View in source") [Ⓣ][1]
+
+*(Number)*: The standard error of the mean.
+
+* * *
+
+
+
+
+
+
+### `Benchmark.prototype.stats.variance`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3520 "View in source") [Ⓣ][1]
+
+*(Number)*: The sample variance.
+
+* * *
+
+
+
+
+
+
+
+
+
+## `Benchmark.prototype.times`
+
+
+
+### `Benchmark.prototype.times`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3529 "View in source") [Ⓣ][1]
+
+*(Object)*: An object of timing data including cycle, elapsed, period, start, and stop.
+
+* * *
+
+
+
+
+
+
+### `Benchmark.prototype.times.cycle`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3537 "View in source") [Ⓣ][1]
+
+*(Number)*: The time taken to complete the last cycle *(secs)*.
+
+* * *
+
+
+
+
+
+
+### `Benchmark.prototype.times.elapsed`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3545 "View in source") [Ⓣ][1]
+
+*(Number)*: The time taken to complete the benchmark *(secs)*.
+
+* * *
+
+
+
+
+
+
+### `Benchmark.prototype.times.period`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3553 "View in source") [Ⓣ][1]
+
+*(Number)*: The time taken to execute the test once *(secs)*.
+
+* * *
+
+
+
+
+
+
+### `Benchmark.prototype.times.timeStamp`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3561 "View in source") [Ⓣ][1]
+
+*(Number)*: A timestamp of when the benchmark started *(ms)*.
+
+* * *
+
+
+
+
+
+
+
+
+
+## `Benchmark.Deferred`
+
+
+
+### `Benchmark.Deferred(clone)`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L445 "View in source") [Ⓣ][1]
+
+The Deferred constructor.
+
+#### Arguments
+1. `clone` *(Object)*: The cloned benchmark instance.
+
+* * *
+
+
+
+
+
+
+
+
+
+## `Benchmark.Deferred.prototype`
+
+
+
+### `Benchmark.Deferred.prototype.benchmark`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3605 "View in source") [Ⓣ][1]
+
+*(Object)*: The deferred benchmark instance.
+
+* * *
+
+
+
+
+
+
+### `Benchmark.Deferred.prototype.cycles`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3613 "View in source") [Ⓣ][1]
+
+*(Number)*: The number of deferred cycles performed while benchmarking.
+
+* * *
+
+
+
+
+
+
+### `Benchmark.Deferred.prototype.elapsed`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3621 "View in source") [Ⓣ][1]
+
+*(Number)*: The time taken to complete the deferred benchmark *(secs)*.
+
+* * *
+
+
+
+
+
+
+### `Benchmark.Deferred.prototype.resolve`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L1188 "View in source") [Ⓣ][1]
+
+*(Unknown)*: Handles cycling/completing the deferred benchmark.
+
+* * *
+
+
+
+
+
+
+### `Benchmark.Deferred.prototype.timeStamp`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3629 "View in source") [Ⓣ][1]
+
+*(Number)*: A timestamp of when the deferred benchmark started *(ms)*.
+
+* * *
+
+
+
+
+
+
+
+
+
+## `Benchmark.Event`
+
+
+
+### `Benchmark.Event(type)`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L461 "View in source") [Ⓣ][1]
+
+The Event constructor.
+
+#### Arguments
+1. `type` *(String|Object)*: The event type.
+
+* * *
+
+
+
+
+
+
+
+
+
+## `Benchmark.Event.prototype`
+
+
+
+### `Benchmark.Event.prototype.aborted`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3645 "View in source") [Ⓣ][1]
+
+*(Boolean)*: A flag to indicate if the emitters listener iteration is aborted.
+
+* * *
+
+
+
+
+
+
+### `Benchmark.Event.prototype.cancelled`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3653 "View in source") [Ⓣ][1]
+
+*(Boolean)*: A flag to indicate if the default action is cancelled.
+
+* * *
+
+
+
+
+
+
+### `Benchmark.Event.prototype.result`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3669 "View in source") [Ⓣ][1]
+
+*(Mixed)*: The return value of the last executed listener.
+
+* * *
+
+
+
+
+
+
+### `Benchmark.Event.prototype.timeStamp`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3685 "View in source") [Ⓣ][1]
+
+*(Number)*: A timestamp of when the event was created *(ms)*.
+
+* * *
+
+
+
+
+
+
+### `Benchmark.Event.prototype.type`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3693 "View in source") [Ⓣ][1]
+
+*(String)*: The event type.
+
+* * *
+
+
+
+
+
+
+
+
+
+## `Benchmark.Event.prototype.currentTarget`
+
+
+
+### `Benchmark.Event.prototype.currentTarget`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3661 "View in source") [Ⓣ][1]
+
+*(Object)*: The object whose listeners are currently being processed.
+
+* * *
+
+
+
+
+
+
+
+
+
+## `Benchmark.Event.prototype.target`
+
+
+
+### `Benchmark.Event.prototype.target`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3677 "View in source") [Ⓣ][1]
+
+*(Object)*: The object to which the event was originally emitted.
+
+* * *
+
+
+
+
+
+
+
+
+
+## `Benchmark.Suite`
+
+
+
+### `Benchmark.Suite(name [, options={}])`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L507 "View in source") [Ⓣ][1]
+
+The Suite constructor.
+
+#### Arguments
+1. `name` *(String)*: A name to identify the suite.
+2. `[options={}]` *(Object)*: Options object.
+
+#### Example
+~~~ js
+// basic usage (the `new` operator is optional)
+var suite = new Benchmark.Suite;
+
+// or using a name first
+var suite = new Benchmark.Suite('foo');
+
+// or with options
+var suite = new Benchmark.Suite('foo', {
+
+  // called when the suite starts running
+  'onStart': onStart,
+
+  // called between running benchmarks
+  'onCycle': onCycle,
+
+  // called when aborted
+  'onAbort': onAbort,
+
+  // called when a test errors
+  'onError': onError,
+
+  // called when reset
+  'onReset': onReset,
+
+  // called when the suite completes running
+  'onComplete': onComplete
+});
+~~~
+
+* * *
+
+
+
+
+
+
+
+
+
+## `Benchmark.Suite.prototype`
+
+
+
+### `Benchmark.Suite.prototype.aborted`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3734 "View in source") [Ⓣ][1]
+
+*(Boolean)*: A flag to indicate if the suite is aborted.
+
+* * *
+
+
+
+
+
+
+### `Benchmark.Suite.prototype.length`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3726 "View in source") [Ⓣ][1]
+
+*(Number)*: The number of benchmarks in the suite.
+
+* * *
+
+
+
+
+
+
+### `Benchmark.Suite.prototype.running`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3742 "View in source") [Ⓣ][1]
+
+*(Boolean)*: A flag to indicate if the suite is running.
+
+* * *
+
+
+
+
+
+
+### `Benchmark.Suite.prototype.abort()`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L1902 "View in source") [Ⓣ][1]
+
+Aborts all benchmarks in the suite.
+
+#### Returns
+*(Object)*: The suite instance.
+
+* * *
+
+
+
+
+
+
+### `Benchmark.Suite.prototype.add(name, fn [, options={}])`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L1962 "View in source") [Ⓣ][1]
+
+Adds a test to the benchmark suite.
+
+#### Arguments
+1. `name` *(String)*: A name to identify the benchmark.
+2. `fn` *(Function|String)*: The test to benchmark.
+3. `[options={}]` *(Object)*: Options object.
+
+#### Returns
+*(Object)*: The benchmark instance.
+
+#### Example
+~~~ js
+// basic usage
+suite.add(fn);
+
+// or using a name first
+suite.add('foo', fn);
+
+// or with options
+suite.add('foo', fn, {
+  'onCycle': onCycle,
+  'onComplete': onComplete
+});
+
+// or name and options
+suite.add('foo', {
+  'fn': fn,
+  'onCycle': onCycle,
+  'onComplete': onComplete
+});
+
+// or options only
+suite.add({
+  'name': 'foo',
+  'fn': fn,
+  'onCycle': onCycle,
+  'onComplete': onComplete
+});
+~~~
+
+* * *
+
+
+
+
+
+
+### `Benchmark.Suite.prototype.clone(options)`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L1981 "View in source") [Ⓣ][1]
+
+Creates a new suite with cloned benchmarks.
+
+#### Arguments
+1. `options` *(Object)*: Options object to overwrite cloned options.
+
+#### Returns
+*(Object)*: The new suite instance.
+
+* * *
+
+
+
+
+
+
+### `Benchmark.Suite.prototype.emit(type)`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L2095 "View in source") [Ⓣ][1]
+
+Executes all registered listeners of the specified event type.
+
+#### Arguments
+1. `type` *(String|Object)*: The event type or object.
+
+#### Returns
+*(Mixed)*: Returns the return value of the last listener executed.
+
+* * *
+
+
+
+
+
+
+### `Benchmark.Suite.prototype.filter(callback)`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L2004 "View in source") [Ⓣ][1]
+
+An `Array#filter` like method.
+
+#### Arguments
+1. `callback` *(Function|String)*: The function/alias called per iteration.
+
+#### Returns
+*(Object)*: A new suite of benchmarks that passed callback filter.
+
+* * *
+
+
+
+
+
+
+### `Benchmark.Suite.prototype.forEach(callback)`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3752 "View in source") [Ⓣ][1]
+
+An `Array#forEach` like method. Callbacks may terminate the loop by explicitly returning `false`.
+
+#### Arguments
+1. `callback` *(Function)*: The function called per iteration.
+
+#### Returns
+*(Object)*: The suite iterated over.
+
+* * *
+
+
+
+
+
+
+### `Benchmark.Suite.prototype.indexOf(value)`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3761 "View in source") [Ⓣ][1]
+
+An `Array#indexOf` like method.
+
+#### Arguments
+1. `value` *(Mixed)*: The value to search for.
+
+#### Returns
+*(Number)*: The index of the matched value or `-1`.
+
+* * *
+
+
+
+
+
+
+### `Benchmark.Suite.prototype.invoke(name [, arg1, arg2, ...])`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3771 "View in source") [Ⓣ][1]
+
+Invokes a method on all benchmarks in the suite.
+
+#### Arguments
+1. `name` *(String|Object)*: The name of the method to invoke OR options object.
+2. `[arg1, arg2, ...]` *(Mixed)*: Arguments to invoke the method with.
+
+#### Returns
+*(Array)*: A new array of values returned from each method invoked.
+
+* * *
+
+
+
+
+
+
+### `Benchmark.Suite.prototype.join([separator=','])`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3780 "View in source") [Ⓣ][1]
+
+Converts the suite of benchmarks to a string.
+
+#### Arguments
+1. `[separator=',']` *(String)*: A string to separate each element of the array.
+
+#### Returns
+*(String)*: The string.
+
+* * *
+
+
+
+
+
+
+### `Benchmark.Suite.prototype.listeners(type)`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L2125 "View in source") [Ⓣ][1]
+
+Returns an array of event listeners for a given type that can be manipulated to add or remove listeners.
+
+#### Arguments
+1. `type` *(String)*: The event type.
+
+#### Returns
+*(Array)*: The listeners array.
+
+* * *
+
+
+
+
+
+
+### `Benchmark.Suite.prototype.map(callback)`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3789 "View in source") [Ⓣ][1]
+
+An `Array#map` like method.
+
+#### Arguments
+1. `callback` *(Function)*: The function called per iteration.
+
+#### Returns
+*(Array)*: A new array of values returned by the callback.
+
+* * *
+
+
+
+
+
+
+### `Benchmark.Suite.prototype.off([type, listener])`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L2158 "View in source") [Ⓣ][1]
+
+Unregisters a listener for the specified event type(s), or unregisters all listeners for the specified event type(s), or unregisters all listeners for all event types.
+
+#### Arguments
+1. `[type]` *(String)*: The event type.
+2. `[listener]` *(Function)*: The function to unregister.
+
+#### Returns
+*(Object)*: The benchmark instance.
+
+#### Example
+~~~ js
+// unregister a listener for an event type
+bench.off('cycle', listener);
+
+// unregister a listener for multiple event types
+bench.off('start cycle', listener);
+
+// unregister all listeners for an event type
+bench.off('cycle');
+
+// unregister all listeners for multiple event types
+bench.off('start cycle complete');
+
+// unregister all listeners for all event types
+bench.off();
+~~~
+
+* * *
+
+
+
+
+
+
+### `Benchmark.Suite.prototype.on(type, listener)`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L2197 "View in source") [Ⓣ][1]
+
+Registers a listener for the specified event type(s).
+
+#### Arguments
+1. `type` *(String)*: The event type.
+2. `listener` *(Function)*: The function to register.
+
+#### Returns
+*(Object)*: The benchmark instance.
+
+#### Example
+~~~ js
+// register a listener for an event type
+bench.on('cycle', listener);
+
+// register a listener for multiple event types
+bench.on('start cycle', listener);
+~~~
+
+* * *
+
+
+
+
+
+
+### `Benchmark.Suite.prototype.pluck(property)`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3798 "View in source") [Ⓣ][1]
+
+Retrieves the value of a specified property from all benchmarks in the suite.
+
+#### Arguments
+1. `property` *(String)*: The property to pluck.
+
+#### Returns
+*(Array)*: A new array of property values.
+
+* * *
+
+
+
+
+
+
+### `Benchmark.Suite.prototype.pop()`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3806 "View in source") [Ⓣ][1]
+
+Removes the last benchmark from the suite and returns it.
+
+#### Returns
+*(Mixed)*: The removed benchmark.
+
+* * *
+
+
+
+
+
+
+### `Benchmark.Suite.prototype.push()`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3814 "View in source") [Ⓣ][1]
+
+Appends benchmarks to the suite.
+
+#### Returns
+*(Number)*: The suite's new length.
+
+* * *
+
+
+
+
+
+
+### `Benchmark.Suite.prototype.reduce(callback, accumulator)`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3833 "View in source") [Ⓣ][1]
+
+An `Array#reduce` like method.
+
+#### Arguments
+1. `callback` *(Function)*: The function called per iteration.
+2. `accumulator` *(Mixed)*: Initial value of the accumulator.
+
+#### Returns
+*(Mixed)*: The accumulator.
+
+* * *
+
+
+
+
+
+
+### `Benchmark.Suite.prototype.reset()`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L2019 "View in source") [Ⓣ][1]
+
+Resets all benchmarks in the suite.
+
+#### Returns
+*(Object)*: The suite instance.
+
+* * *
+
+
+
+
+
+
+### `Benchmark.Suite.prototype.reverse()`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L638 "View in source") [Ⓣ][1]
+
+Rearrange the host array's elements in reverse order.
+
+#### Returns
+*(Array)*: The reversed array.
+
+* * *
+
+
+
+
+
+
+### `Benchmark.Suite.prototype.run([options={}])`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L2056 "View in source") [Ⓣ][1]
+
+Runs the suite.
+
+#### Arguments
+1. `[options={}]` *(Object)*: Options object.
+
+#### Returns
+*(Object)*: The suite instance.
+
+#### Example
+~~~ js
+// basic usage
+suite.run();
+
+// or with options
+suite.run({ 'async': true, 'queued': true });
+~~~
+
+* * *
+
+
+
+
+
+
+### `Benchmark.Suite.prototype.shift()`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L671 "View in source") [Ⓣ][1]
+
+Removes the first element of the host array and returns it.
+
+#### Returns
+*(Mixed)*: The first element of the array.
+
+* * *
+
+
+
+
+
+
+### `Benchmark.Suite.prototype.slice(start, end)`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L684 "View in source") [Ⓣ][1]
+
+Creates an array of the host array's elements from the start index up to, but not including, the end index.
+
+#### Arguments
+1. `start` *(Number)*: The starting index.
+2. `end` *(Number)*: The end index.
+
+#### Returns
+*(Array)*: The new array.
+
+* * *
+
+
+
+
+
+
+### `Benchmark.Suite.prototype.sort([compareFn=null])`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3823 "View in source") [Ⓣ][1]
+
+Sorts the benchmarks of the suite.
+
+#### Arguments
+1. `[compareFn=null]` *(Function)*: A function that defines the sort order.
+
+#### Returns
+*(Object)*: The sorted suite.
+
+* * *
+
+
+
+
+
+
+### `Benchmark.Suite.prototype.splice(start, deleteCount [, val1, val2, ...])`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L714 "View in source") [Ⓣ][1]
+
+Allows removing a range of elements and/or inserting elements into the host array.
+
+#### Arguments
+1. `start` *(Number)*: The start index.
+2. `deleteCount` *(Number)*: The number of elements to delete.
+3. `[val1, val2, ...]` *(Mixed)*: values to insert at the `start` index.
+
+#### Returns
+*(Array)*: An array of removed elements.
+
+* * *
+
+
+
+
+
+
+### `Benchmark.Suite.prototype.unshift()`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L749 "View in source") [Ⓣ][1]
+
+Appends arguments to the host array.
+
+#### Returns
+*(Number)*: The new length.
+
+* * *
+
+
+
+
+
+
+
+
+
+## `Benchmark.Suite.options`
+
+
+
+### `Benchmark.Suite.options`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3705 "View in source") [Ⓣ][1]
+
+*(Object)*: The default options copied by suite instances.
+
+* * *
+
+
+
+
+
+
+### `Benchmark.Suite.options.name`
+# [Ⓢ](https://github.com/bestiejs/benchmark.js/blob/master/benchmark.js#L3713 "View in source") [Ⓣ][1]
+
+*(String)*: The name of the suite.
+
+* * *
+
+
+
+
+
+
+
+
+
+
+  [1]: #Benchmark "Jump back to the TOC."
\ No newline at end of file
diff --git a/cep/node_modules/benchmark/package.json b/cep/node_modules/benchmark/package.json
new file mode 100644
index 0000000..3b4183c
--- /dev/null
+++ b/cep/node_modules/benchmark/package.json
@@ -0,0 +1,98 @@
+{
+  "_args": [
+    [
+      {
+        "raw": "benchmark@1.0.0",
+        "scope": null,
+        "escapedName": "benchmark",
+        "name": "benchmark",
+        "rawSpec": "1.0.0",
+        "spec": "1.0.0",
+        "type": "version"
+      },
+      "D:\\repositories\\tk-framework-adobe\\extensions\\http_server\\node_modules\\socket.io-adapter\\node_modules\\socket.io-parser"
+    ]
+  ],
+  "_from": "benchmark@1.0.0",
+  "_id": "benchmark@1.0.0",
+  "_inCache": true,
+  "_installable": true,
+  "_location": "/benchmark",
+  "_phantomChildren": {},
+  "_requested": {
+    "raw": "benchmark@1.0.0",
+    "scope": null,
+    "escapedName": "benchmark",
+    "name": "benchmark",
+    "rawSpec": "1.0.0",
+    "spec": "1.0.0",
+    "type": "version"
+  },
+  "_requiredBy": [
+    "/socket.io-adapter/socket.io-parser",
+    "/socket.io-parser"
+  ],
+  "_resolved": "https://registry.npmjs.org/benchmark/-/benchmark-1.0.0.tgz",
+  "_shasum": "2f1e2fa4c359f11122aa183082218e957e390c73",
+  "_shrinkwrap": null,
+  "_spec": "benchmark@1.0.0",
+  "_where": "D:\\repositories\\tk-framework-adobe\\extensions\\http_server\\node_modules\\socket.io-adapter\\node_modules\\socket.io-parser",
+  "author": {
+    "name": "Mathias Bynens",
+    "email": "mathias@benchmarkjs.com",
+    "url": "http://mathiasbynens.be/"
+  },
+  "bugs": {
+    "url": "https://github.com/bestiejs/benchmark.js/issues",
+    "email": "bugs@benchmarkjs.com"
+  },
+  "dependencies": {},
+  "description": "A benchmarking library that works on nearly all JavaScript platforms, supports high-resolution timers, and returns statistically significant results.",
+  "devDependencies": {},
+  "directories": {
+    "doc": "./doc",
+    "test": "./test"
+  },
+  "dist": {
+    "shasum": "2f1e2fa4c359f11122aa183082218e957e390c73",
+    "tarball": "https://registry.npmjs.org/benchmark/-/benchmark-1.0.0.tgz"
+  },
+  "engines": [
+    "node",
+    "rhino"
+  ],
+  "homepage": "http://benchmarkjs.com/",
+  "keywords": [
+    "benchmark",
+    "narwhal",
+    "node",
+    "performance",
+    "ringo",
+    "speed"
+  ],
+  "licenses": [
+    {
+      "type": "MIT",
+      "url": "http://mths.be/mit"
+    }
+  ],
+  "main": "benchmark",
+  "maintainers": [
+    {
+      "name": "jdalton",
+      "email": "john@fusejs.com"
+    },
+    {
+      "name": "mathias",
+      "email": "mathias@qiwi.be"
+    }
+  ],
+  "name": "benchmark",
+  "optionalDependencies": {},
+  "readme": "ERROR: No README data found!",
+  "repository": {
+    "type": "git",
+    "url": "git+https://github.com/bestiejs/benchmark.js.git"
+  },
+  "version": "1.0.0"
+}
diff --git a/cep/node_modules/benchmark/test/run-test.sh b/cep/node_modules/benchmark/test/run-test.sh
new file mode 100644
index 0000000..43424e4
--- /dev/null
+++ b/cep/node_modules/benchmark/test/run-test.sh
@@ -0,0 +1,9 @@
+cd "$(dirname "$0")"
+for cmd in rhino ringo narwhal node; do
+	echo ""
+	echo "Testing in $cmd..."
+	$cmd test.js
+done
+echo ""
+echo "Testing in a browser..."
+open index.html
diff --git a/cep/node_modules/benchmark/test/test.js b/cep/node_modules/benchmark/test/test.js
new file mode 100644
index 0000000..d694494
--- /dev/null
+++ b/cep/node_modules/benchmark/test/test.js
@@ -0,0 +1,2074 @@
+;(function(window, undefined) {
+  'use strict';
+
+  /** Use a single load function */
+  var load = typeof require == 'function' ? require : window.load;
+
+  /** The `platform` object to check */
+  var platform =
+    window.platform ||
+    load('../vendor/platform.js/platform.js') ||
+    window.platform;
+
+  /** The unit testing framework */
+  var QUnit =
+    window.QUnit || (
+      window.setTimeout || (window.addEventListener = window.setTimeout = / /),
+      window.QUnit = load('../vendor/qunit/qunit/qunit' + (platform.name == 'Narwhal' ? '-1.8.0' : '') + '.js') || window.QUnit,
+      load('../vendor/qunit-clib/qunit-clib.js'),
+      (window.addEventListener || 0).test && delete window.addEventListener,
+      window.QUnit
+    );
+
+  /** The `Benchmark` constructor to test */
+  var Benchmark =
+    window.Benchmark || (
+      Benchmark = load('../benchmark.js') || window.Benchmark,
+      Benchmark.Benchmark || Benchmark
+    );
+
+  /** API shortcut */
+  var forOwn = Benchmark.forOwn;
+
+  /** Used to get property descriptors */
+  var getDescriptor = Object.getOwnPropertyDescriptor;
+
+  /** Used to set property descriptors */
+  var setDescriptor = Object.defineProperty;
+
+  /** Shortcut used to convert array-like objects to arrays */
+  var slice = [].slice;
+
+  /** Used to resolve a value's internal [[Class]] */
+  var toString = {}.toString;
+
+  /** Used to check problem JScript properties (a.k.a. the [[DontEnum]] bug) */
+  var shadowed = {
+    'constructor': 1,
+    'hasOwnProperty': 2,
+    'isPrototypeOf': 3,
+    'propertyIsEnumerable': 4,
+    'toLocaleString': 5,
+    'toString': 6,
+    'valueOf': 7
+  };
+
+  /** Used to flag environments/features */
+  var support = {
+    'descriptors': !!function() {
+      try {
+        var o = {};
+        return (setDescriptor(o, o, o), 'value' in getDescriptor(o, o));
+      } catch(e) { }
+    }()
+  };
+
+  /*--------------------------------------------------------------------------*/
+
+  /**
+   * Skips a given number of tests with a passing result.
+   *
+   * @private
+   * @param {Number} [count=1] The number of tests to skip.
+   */
+  function skipTest(count) {
+    count || (count = 1);
+    while (count--) {
+      ok(true, 'test skipped');
+    }
+  }
+
+  /*--------------------------------------------------------------------------*/
+
+  // init Benchmark.options.minTime
+  Benchmark(function() { throw 0; }).run();
+
+  // set a shorter max time
+  Benchmark.options.maxTime = Benchmark.options.minTime * 5;
+
+  // explicitly call `QUnit.module()` instead of `module()`
+  // in case we are in a CLI environment
+  QUnit.module('Benchmark');
+
+  (function() {
+    test('has the default `Benchmark.platform` value', function() {
+      if (window.document) {
+        equal(String(Benchmark.platform), navigator.userAgent);
+      } else {
+        skipTest(1)
+      }
+    });
+
+    test('supports loading Benchmark.js as a module', function() {
+      if (window.document && window.require) {
+        equal((Benchmark2 || {}).version, Benchmark.version);
+      } else {
+        skipTest(1)
+      }
+    });
+
+    test('supports loading Platform.js as a module', function() {
+      if (window.document && window.require) {
+        var platform = (Benchmark2 || {}).platform || {};
+        equal(typeof platform.name, 'string');
+      } else {
+        skipTest(1)
+      }
+    });
+  }());
+
+  /*--------------------------------------------------------------------------*/
+
+  QUnit.module('Benchmark constructor');
+
+  (function() {
+    test('creates a new instance when called without the `new` operator', function() {
+      ok(Benchmark() instanceof Benchmark);
+    });
+
+    test('supports passing an options object', function() {
+      var bench = Benchmark({ 'name': 'foo', 'fn': function() { } });
+      ok(bench.fn && bench.name == 'foo');
+    });
+
+    test('supports passing a "name" and "fn" argument', function() {
+      var bench = Benchmark('foo', function() { });
+      ok(bench.fn && bench.name == 'foo');
+    });
+
+    test('supports passing a "name" argument and an options object', function() {
+      var bench = Benchmark('foo', { 'fn': function() { } });
+      ok(bench.fn && bench.name == 'foo');
+    });
+
+    test('supports passing a "name" argument and an options object', function() {
+      var bench = Benchmark('foo', function() { }, { 'id': 'bar' });
+      ok(bench.fn && bench.name == 'foo' && bench.id == 'bar');
+    });
+
+    test('supports passing an empy string for the "fn" options property', function() {
+      var bench = Benchmark({ 'fn': '' }).run();
+      ok(!bench.error);
+    });
+
+    test('detects dead code', function() {
+      var bench = Benchmark(function() { }).run();
+      ok(/setup\(\)/.test(bench.compiled) ? !bench.error : bench.error);
+    });
+  }());
+
+  /*--------------------------------------------------------------------------*/
+
+  QUnit.module('Benchmark compilation');
+
+  (function() {
+    test('compiles using the default `Function#toString`', function() {
+      var bench = Benchmark({
+        'setup': function() { var a = 1; },
+        'fn': function() { throw a; },
+        'teardown': function() { a = 2; }
+      }).run();
+
+      var compiled = bench.compiled;
+      if (/setup\(\)/.test(compiled)) {
+        skipTest();
+      }
+      else {
+        ok(/var a\s*=\s*1/.test(compiled) && /throw a/.test(compiled) && /a\s*=\s*2/.test(compiled));
+      }
+    });
+
+    test('compiles using a custom "toString" method', function() {
+      var bench = Benchmark({
+        'setup': function() { },
+        'fn': function() { },
+        'teardown': function() { }
+      });
+
+      bench.setup.toString = function() { return 'var a = 1;' };
+      bench.fn.toString = function() { return 'throw a;' };
+      bench.teardown.toString = function() { return 'a = 2;' };
+      bench.run();
+
+      var compiled = bench.compiled;
+      if (/setup\(\)/.test(compiled)) {
+        skipTest();
+      }
+      else {
+        ok(/var a\s*=\s*1/.test(compiled) && /throw a/.test(compiled) && /a\s*=\s*2/.test(compiled));
+      }
+    });
+
+    test('compiles using a string value', function() {
+      var bench = Benchmark({
+        'setup': 'var a = 1;',
+        'fn': 'throw a;',
+        'teardown': 'a = 2;'
+      }).run();
+
+      var compiled = bench.compiled;
+      if (/setup\(\)/.test(compiled)) {
+        skipTest();
+      }
+      else {
+        ok(/var a\s*=\s*1/.test(compiled) && /throw a/.test(compiled) && /a\s*=\s*2/.test(compiled));
+      }
+    });
+  }());
+
+  /*--------------------------------------------------------------------------*/
+
+  QUnit.module('Benchmark test binding');
+
+  (function() {
+    var count = 0;
+
+    var tests = {
+      'inlined "setup", "fn", and "teardown"': (
+        'if(/ops/.test(this))this._fn=true;'
+      ),
+      'called "fn" and inlined "setup"/"teardown" reached by error': function() {
+        count++;
+        if (/ops/.test(this)) {
+          this._fn = true;
+        }
+      },
+      'called "fn" and inlined "setup"/"teardown" reached by `return` statement': function() {
+        if (/ops/.test(this)) {
+          this._fn = true;
+        }
+        return;
+      }
+    };
+
+    forOwn(tests, function(fn, title) {
+      test('has correct binding for ' + title, function() {
+        var bench = Benchmark({
+          'setup': 'if(/ops/.test(this))this._setup=true;',
+          'fn': fn,
+          'teardown': 'if(/ops/.test(this))this._teardown=true;',
+          'onCycle': function() { this.abort(); }
+        }).run();
+
+        var compiled = bench.compiled;
+        if (/setup\(\)/.test(compiled)) {
+          skipTest(3);
+        }
+        else {
+          ok(bench._setup, 'correct binding for "setup"');
+          ok(bench._fn, 'correct binding for "fn"');
+          ok(bench._teardown, 'correct binding for "teardown"');
+        }
+      });
+    });
+  }());
+
+  /*--------------------------------------------------------------------------*/
+
+  QUnit.module('Benchmark.deepClone');
+
+  (function() {
+    function createCircularObject() {
+      var result = {
+        'foo': { 'b': { 'foo': { 'c': { } } } },
+        'bar': { }
+      };
+
+      result.foo.b.foo.c.foo = result;
+      result.bar.b = result.foo.b;
+      return result;
+    }
+
+    function Klass() {
+      this.a = 1;
+    }
+
+    Klass.prototype = { 'b': 1 };
+
+    var notCloneable = {
+      'an arguments object': arguments,
+      'an element': window.document && document.body,
+      'a function': Klass,
+      'a Klass instance': new Klass
+    };
+
+    var objects = {
+      'an array': ['a', 'b', 'c', ''],
+      'an array-like-object': { '0': 'a', '1': 'b', '2': 'c',  '3': '', 'length': 5 },
+      'boolean': false,
+      'boolean object': Object(false),
+      'an object': { 'a': 0, 'b': 1, 'c': 3 },
+      'an object with object values': { 'a': /a/, 'b': ['B'], 'c': { 'C': 1 } },
+      'null': null,
+      'a number': 3,
+      'a number object': Object(3),
+      'a regexp': /x/gim,
+      'a string': 'x',
+      'a string object': Object('x'),
+      'undefined': undefined
+    };
+
+    objects['an array'].length = 5;
+
+    forOwn(objects, function(object, key) {
+      test('clones ' + key + ' correctly', function() {
+        var kind = toString.call(object),
+            clone = Benchmark.deepClone(object);
+
+        if (object == null) {
+          equal(clone, object);
+        } else {
+          deepEqual(clone.valueOf(), object.valueOf());
+        }
+        if (object === Object(object)) {
+          ok(clone !== object);
+        } else {
+          skipTest();
+        }
+      });
+    });
+
+    forOwn(notCloneable, function(object, key) {
+      test('does not clone ' + key, function() {
+        ok(Benchmark.deepClone(object) === object);
+      });
+    });
+
+    test('clones using Klass#deepClone', function() {
+      var object = new Klass;
+      Klass.prototype.deepClone = function() { return new Klass; };
+
+      var clone = Benchmark.deepClone(object);
+      ok(clone !== object && clone instanceof Klass);
+
+      delete Klass.prototype.clone;
+    });
+
+    test('clones problem JScript properties', function() {
+      var clone = Benchmark.deepClone(shadowed);
+      deepEqual(clone, shadowed);
+    });
+
+    test('clones string object with custom property', function() {
+      var object = new String('x');
+      object.x = 1;
+
+      var clone = Benchmark.deepClone(object);
+      ok(clone == 'x' && typeof clone == 'object' && clone.x === 1 && toString.call(clone) == '[object String]');
+    });
+
+    test('clones objects with circular references', function() {
+      var object = createCircularObject(),
+          clone = Benchmark.deepClone(object);
+
+      ok(clone.bar.b === clone.foo.b && clone === clone.foo.b.foo.c.foo && clone !== object);
+    });
+
+    test('clones non-extensible objects with circular references', function() {
+      if (Object.preventExtensions) {
+        var object = Object.preventExtensions(createCircularObject());
+        Object.preventExtensions(object.bar.b);
+
+        var clone = Benchmark.deepClone(object);
+        ok(clone.bar.b === clone.foo.b && clone === clone.foo.b.foo.c.foo && clone !== object);
+      } else {
+        skipTest(1)
+      }
+    });
+
+    test('clones sealed objects with circular references', function() {
+      if (Object.seal) {
+        var object = Object.seal(createCircularObject());
+        Object.seal(object.bar.b);
+
+        var clone = Benchmark.deepClone(object);
+        ok(clone.bar.b === clone.foo.b && clone === clone.foo.b.foo.c.foo && clone !== object);
+      } else {
+        skipTest(1)
+      }
+    });
+
+    test('clones frozen objects with circular references', function() {
+      if (Object.freeze) {
+        var object = Object.freeze(createCircularObject());
+        Object.freeze(object.bar.b);
+
+        var clone = Benchmark.deepClone(object);
+        ok(clone.bar.b === clone.foo.b && clone === clone.foo.b.foo.c.foo && clone !== object);
+      } else {
+        skipTest(1)
+      }
+    });
+
+    test('clones objects with custom descriptors and circular references', function() {
+      var accessor,
+          descriptor;
+
+      if (support.descriptors) {
+        var object = setDescriptor({}, 'foo', {
+          'configurable': true,
+          'value': setDescriptor({}, 'b', {
+            'writable': true,
+            'value': setDescriptor({}, 'foo', {
+              'get': function() { return accessor; },
+              'set': function(value) { accessor = value; }
+            })
+          })
+        });
+
+        setDescriptor(object, 'bar', { 'value': {} });
+        object.foo.b.foo = { 'c': object };
+        object.bar.b = object.foo.b;
+
+        var clone = Benchmark.deepClone(object);
+        ok(clone !== object &&
+          clone.bar.b === clone.foo.b &&
+          clone !== clone.foo.b.foo.c.foo &&
+          (descriptor = getDescriptor(clone, 'foo')) &&
+          descriptor.configurable && !(descriptor.enumerable && descriptor.writable) &&
+          (descriptor = getDescriptor(clone.foo, 'b')) &&
+          descriptor.writable && !(descriptor.configurable && descriptor.enumerable) &&
+          (descriptor = getDescriptor(clone.foo.b, 'foo')) &&
+          descriptor.get && descriptor.set &&
+          (descriptor = getDescriptor(clone.foo.b, 'foo')) &&
+          !(descriptor.configurable && descriptor.enumerable && descriptor.writable) &&
+          (descriptor = getDescriptor(clone, 'bar')) &&
+          !(descriptor.configurable && descriptor.enumerable && descriptor.writable));
+      }
+      else {
+        skipTest(1)
+      }
+    });
+  }());
+
+  /*--------------------------------------------------------------------------*/
+
+  QUnit.module('Benchmark.each');
+
+  (function() {
+    var xpathResult;
+
+    var objects = {
+      'array': ['a', 'b', 'c', ''],
+      'array-like-object': { '0': 'a', '1': 'b', '2': 'c',  '3': '', 'length': 5 },
+      'xpath snapshot': null
+    };
+
+    if (window.document && document.evaluate) {
+      xpathResult = [document.documentElement, document.getElementsByTagName('head')[0], document.body];
+      objects['xpath snapshot'] = document.evaluate('//*[self::html or self::head or self::body]', document, null, 7, null);
+    }
+
+    objects.array.length = 5;
+
+    forOwn(objects, function(object, key) {
+      test('passes the correct arguments when passing an ' + key, function() {
+        if (object) {
+          var args
+          Benchmark.each(object, function() {
+            args || (args = slice.call(arguments));
+          });
+
+          if (key == 'xpath snapshot') {
+            ok(args[0] === xpathResult[0]);
+          } else {
+            equal(args[0], 'a');
+          }
+          equal(args[1], 0);
+          ok(args[2] === object);
+        }
+        else {
+          skipTest(3);
+        }
+      });
+
+      test('returns the passed object when passing an ' + key, function() {
+        if (object) {
+          var actual = Benchmark.each(object, function() { });
+          ok(actual === object);
+        }
+        else {
+          skipTest();
+        }
+      });
+
+      test('iterates over all indexes when passing an ' + key, function() {
+        if (object) {
+          var values = [];
+          Benchmark.each(object, function(value) {
+            values.push(value);
+          });
+
+          deepEqual(values, key == 'xpath snapshot' ? xpathResult : ['a', 'b', 'c', '']);
+        }
+        else {
+          skipTest();
+        }
+      });
+
+      test('exits early when returning `false` when passing an ' + key, function() {
+        if (object) {
+          var values = [];
+          Benchmark.each(object, function(value) {
+            values.push(value);
+            return values.length < 2;
+          });
+
+          deepEqual(values, key == 'xpath snapshot' ? xpathResult.slice(0, 2) : ['a', 'b']);
+        }
+        else {
+          skipTest();
+        }
+      });
+    });
+
+    test('passes the third callback argument as an object', function() {
+      var thirdArg;
+      Benchmark.each('hello', function(value, index, object) {
+        thirdArg = object;
+      });
+
+      ok(thirdArg && typeof thirdArg == 'object');
+    });
+
+    test('iterates over strings by index', function() {
+      var values = [];
+      Benchmark.each('hello', function(value) {
+        values.push(value)
+      });
+
+      deepEqual(values, ['h', 'e', 'l', 'l', 'o']);
+    });
+  }());
+
+  /*--------------------------------------------------------------------------*/
+
+  QUnit.module('Benchmark.extend');
+
+  (function() {
+    test('allows no source argument', function() {
+      var object = {};
+      equal(Benchmark.extend(object), object);
+    });
+
+    test('allows a single source argument', function() {
+      var source = { 'x': 1, 'y': 1 },
+          actual = Benchmark.extend({}, source);
+
+      deepEqual(Benchmark.extend({}, source), { 'x': 1, 'y': 1 });
+    });
+
+    test('allows multiple source arguments', function() {
+      var source1 = { 'x': 1, 'y': 1 },
+          source2 = { 'y': 2, 'z': 2 },
+          actual = Benchmark.extend({}, source1, source2);
+
+      deepEqual(actual, { 'x': 1, 'y': 2, 'z': 2 });
+    });
+
+    test('will add inherited source properties', function() {
+      function Source() { }
+      Source.prototype.x = 1;
+      deepEqual(Benchmark.extend({}, new Source), { 'x': 1 });
+    });
+
+    test('will add problem JScript properties', function() {
+      deepEqual(Benchmark.extend({}, shadowed), shadowed);
+    });
+  }());
+
+  /*--------------------------------------------------------------------------*/
+
+  QUnit.module('Benchmark.filter');
+
+  (function() {
+    var objects = {
+      'array': ['a', 'b', 'c', ''],
+      'array-like-object': { '0': 'a', '1': 'b', '2': 'c',  '3': '', 'length': 5 }
+    };
+
+    objects.array.length = 5;
+
+    forOwn(objects, function(object, key) {
+      test('passes the correct arguments when passing an ' + key, function() {
+        var args;
+        Benchmark.filter(object, function() {
+          args || (args = slice.call(arguments));
+        });
+
+        deepEqual(args, ['a', 0, object]);
+      });
+
+      test('produces the correct result when passing an ' + key, function() {
+        var actual = Benchmark.filter(object, function(value, index) {
+          return index > 0;
+        });
+
+        deepEqual(actual, ['b', 'c', '']);
+      });
+
+      test('iterates over sparse ' + key + 's correctly', function() {
+        var actual = Benchmark.filter(object, function(value) {
+          return value === undefined;
+        });
+
+        deepEqual(actual, []);
+      });
+    });
+  }());
+
+  /*--------------------------------------------------------------------------*/
+
+  QUnit.module('Benchmark.forOwn');
+
+  (function() {
+    function fn() {
+      // no-op
+    }
+
+    function KlassA() {
+      this.a = 1;
+      this.b = 2;
+      this.c = 3;
+    }
+
+    function KlassB() {
+      this.a = 1;
+      this.constructor = 2;
+      this.hasOwnProperty = 3;
+      this.isPrototypeOf = 4;
+      this.propertyIsEnumerable = 5;
+      this.toLocaleString = 6;
+      this.toString = 7;
+      this.valueOf = 8;
+    }
+
+    function KlassC() {
+      // no-op
+    }
+
+    fn.a = 1;
+    fn.b = 2;
+    fn.c = 3;
+
+    KlassC.prototype.a = 1;
+    KlassC.prototype.b = 2;
+    KlassC.prototype.c = 3;
+
+    var objects = {
+      'an arguments object': arguments,
+      'a function': fn,
+      'an object': new KlassA,
+      'an object shadowing properties on Object.prototype': new KlassB,
+      'a prototype object': KlassC.prototype,
+      'a string': 'abc'
+    };
+
+    forOwn(objects, function(object, key) {
+      test('passes the correct arguments when passing ' + key, function() {
+        var args;
+        Benchmark.forOwn(object, function() {
+          args || (args = slice.call(arguments));
+        });
+
+        equal(typeof args[0], key == 'a string' ? 'string' : 'number');
+        equal(typeof args[1], 'string');
+        equal(args[2] && typeof args[2], key == 'a function' ? 'function' : 'object');
+      });
+
+      test('returns the passed object when passing ' + key, function() {
+        var actual = Benchmark.forOwn(object, function() { });
+        deepEqual(actual, object);
+      });
+
+      test('iterates over own properties when passing ' + key, function() {
+        var values = [];
+        Benchmark.forOwn(object, function(value) {
+          values.push(value);
+        });
+
+        if (object instanceof KlassB) {
+          deepEqual(values.sort(), [1, 2, 3, 4, 5, 6, 7, 8]);
+        } else if (key == 'a string') {
+          deepEqual(values, ['a', 'b', 'c']);
+        } else {
+          deepEqual(values.sort(), [1, 2, 3]);
+        }
+      });
+
+      test('exits early when returning `false` when passing ' + key, function() {
+        var values = [];
+        Benchmark.forOwn(object, function(value) {
+          values.push(value);
+          return false;
+        });
+
+        equal(values.length, 1);
+      });
+
+      if (object instanceof KlassB) {
+        test('exits correctly when transitioning to the JScript [[DontEnum]] fix', function() {
+          var values = [];
+          Benchmark.forOwn(object, function(value) {
+            values.push(value);
+            return values.length < 2;
+          });
+
+          equal(values.length, 2);
+        });
+      }
+    });
+  }(1, 2, 3));
+
+  /*--------------------------------------------------------------------------*/
+
+  QUnit.module('Benchmark.formatNumber');
+
+  (function() {
+    test('formats a million correctly', function() {
+      equal(Benchmark.formatNumber(1e6), '1,000,000');
+    });
+
+    test('formats less than 100 correctly', function() {
+      equal(Benchmark.formatNumber(23), '23');
+    });
+
+    test('formats numbers with decimal values correctly', function() {
+      equal(Benchmark.formatNumber(1234.56), '1,234.56');
+    });
+
+    test('formats negative numbers correctly', function() {
+      equal(Benchmark.formatNumber(-1234.56), '-1,234.56');
+    });
+  }());
+
+  /*--------------------------------------------------------------------------*/
+
+  QUnit.module('Benchmark.hasKey');
+
+  (function() {
+    test('returns `true` for own properties', function() {
+      var object = { 'x': 1 };
+      equal(Benchmark.hasKey(object, 'x'), true);
+    });
+
+    test('returns `false` for inherited properties', function() {
+      equal(Benchmark.hasKey({}, 'toString'), false);
+    });
+
+    test('doesn\'t use an object\'s `hasOwnProperty` method', function() {
+      var object = { 'hasOwnProperty': function() { return true; } };
+      equal(Benchmark.hasKey(object, 'x'), false);
+    });
+  }());
+
+  /*--------------------------------------------------------------------------*/
+
+  QUnit.module('Benchmark.indexOf');
+
+  (function() {
+    var objects = {
+      'array': ['a', 'b', 'c', ''],
+      'array-like-object': { '0': 'a', '1': 'b', '2': 'c',  '3': '', 'length': 5 }
+    };
+
+    objects.array.length = 5;
+
+    forOwn(objects, function(object, key) {
+      test('produces the correct result when passing an ' + key, function() {
+        equal(Benchmark.indexOf(object, 'b'), 1);
+      });
+
+      test('matches values by strict equality when passing an ' + key, function() {
+        equal(Benchmark.indexOf(object, new String('b')), -1);
+      });
+
+      test('iterates over sparse ' + key + 's correctly', function() {
+        equal(Benchmark.indexOf(object, undefined), -1);
+      });
+    });
+
+    test('searches from the given `fromIndex`', function() {
+      var array = ['a', 'b', 'c', 'a'];
+      equal(Benchmark.indexOf(array, 'a', 1), 3);
+    });
+
+    test('handles extreme negative `fromIndex` values correctly', function() {
+      var array = ['a'];
+      array['-1'] = 'z';
+      equal(Benchmark.indexOf(array, 'z', -2), -1);
+    });
+
+    test('handles extreme positive `fromIndex` values correctly', function() {
+      var object = { '0': 'a', '1': 'b', '2': 'c', 'length': 2 };
+      equal(Benchmark.indexOf(object, 'c', 2), -1);
+    });
+  }());
+
+  /*--------------------------------------------------------------------------*/
+
+  QUnit.module('Benchmark.interpolate');
+
+  (function() {
+    test('replaces tokens correctly', function() {
+      var actual = Benchmark.interpolate('#{greeting} #{location}.', {
+        'greeting': 'Hello',
+        'location': 'world'
+      });
+
+      equal(actual, 'Hello world.');
+    });
+
+    test('ignores inherited object properties', function() {
+      var actual = Benchmark.interpolate('x#{toString}', {});
+      equal(actual, 'x#{toString}');
+    });
+
+    test('allows for no template object', function() {
+      var actual = Benchmark.interpolate('x');
+      equal(actual, 'x');
+    });
+
+    test('replaces duplicate tokens', function() {
+      var actual = Benchmark.interpolate('#{x}#{x}#{x}', { 'x': 'a' });
+      equal(actual, 'aaa');
+    });
+
+    test('handles keys containing RegExp special characters', function() {
+      var actual = Benchmark.interpolate('#{.*+?^=!:${}()|[]\\/}', { '.*+?^=!:${}()|[]\\/': 'x' });
+      equal(actual, 'x');
+    });
+  }());
+
+  /*--------------------------------------------------------------------------*/
+
+  QUnit.module('Benchmark.invoke');
+
+  (function() {
+    var objects = {
+      'array': ['a', ['b'], 'c', null],
+      'array-like-object': { '0': 'a', '1': ['b'], '2': 'c',  '3': null, 'length': 5 }
+    };
+
+    objects.array.length = 5;
+
+    forOwn(objects, function(object, key) {
+      test('produces the correct result when passing an ' + key, function() {
+        var actual = Benchmark.invoke(object, 'concat');
+        deepEqual(actual, ['a', ['b'], 'c', undefined, undefined]);
+        equal('4' in actual, false);
+      });
+
+      test('passes the correct arguments to the invoked method when passing an ' + key, function() {
+        var actual = Benchmark.invoke(object, 'concat', 'x', 'y', 'z');
+        deepEqual(actual, ['axyz', ['b', 'x', 'y', 'z'], 'cxyz', undefined, undefined]);
+        equal('4' in actual, false);
+      });
+
+      test('handles options object with callbacks correctly when passing an ' + key, function() {
+        function callback() {
+          callbacks.push(slice.call(arguments));
+        }
+
+        var callbacks = [];
+        var actual = Benchmark.invoke(object, {
+          'name': 'concat',
+          'args': ['x', 'y', 'z'],
+          'onStart': callback,
+          'onCycle': callback,
+          'onComplete': callback
+        });
+
+        deepEqual(actual, ['axyz', ['b', 'x', 'y', 'z'], 'cxyz', undefined, undefined]);
+        equal('4' in actual, false);
+
+        equal(callbacks[0].length, 1);
+        equal(callbacks[0][0].target, 'a');
+        deepEqual(callbacks[0][0].currentTarget, object);
+        equal(callbacks[0][0].type, 'start');
+        equal(callbacks[1][0].type, 'cycle');
+        equal(callbacks[5][0].type, 'complete');
+      });
+
+      test('supports queuing when passing an ' + key, function() {
+        var lengths = [];
+        var actual = Benchmark.invoke(object, {
+          'name': 'concat',
+          'queued': true,
+          'args': 'x',
+          'onCycle': function() {
+            lengths.push(object.length);
+          }
+        });
+
+        deepEqual(lengths, [5, 4, 3, 2]);
+        deepEqual(actual, ['ax', ['b', 'x'], 'cx', undefined, undefined]);
+      });
+    });
+  }());
+
+  /*--------------------------------------------------------------------------*/
+
+  QUnit.module('Benchmark.join');
+
+  (function() {
+    var objects = {
+      'array': ['a', 'b', ''],
+      'array-like-object': { '0': 'a', '1': 'b', '2': '', 'length': 4 },
+      'object': { 'a': '0', 'b': '1', '': '2' }
+    };
+
+    objects.array.length = 4;
+
+    forOwn(objects, function(object, key) {
+      test('joins correctly using the default separator when passing an ' + key, function() {
+        equal(Benchmark.join(object), key == 'object' ? 'a: 0,b: 1,: 2' : 'a,b,');
+      });
+
+      test('joins correctly using a custom separator when passing an ' + key, function() {
+        equal(Benchmark.join(object, '+', '@'), key == 'object' ? 'a@0+b@1+@2' :  'a+b+');
+      });
+    });
+  }());
+
+  /*--------------------------------------------------------------------------*/
+
+  QUnit.module('Benchmark.map');
+
+  (function() {
+    var objects = {
+      'array': ['a', 'b', 'c', ''],
+      'array-like-object': { '0': 'a', '1': 'b', '2': 'c',  '3': '', 'length': 5 }
+    };
+
+    objects.array.length = 5;
+
+    forOwn(objects, function(object, key) {
+      test('passes the correct arguments when passing an ' + key, function() {
+        var args;
+        Benchmark.map(object, function() {
+          args || (args = slice.call(arguments));
+        });
+
+        deepEqual(args, ['a', 0, object]);
+      });
+
+      test('produces the correct result when passing an ' + key, function() {
+        var actual = Benchmark.map(object, function(value, index) {
+          return value + index;
+        });
+
+        deepEqual(actual, ['a0', 'b1', 'c2', '3', undefined]);
+        equal('4' in actual, false);
+      });
+
+      test('produces an array of the correct length for sparse ' + key + 's', function() {
+        equal(Benchmark.map(object, function() { }).length, 5);
+      });
+    });
+  }());
+
+  /*--------------------------------------------------------------------------*/
+
+  QUnit.module('Benchmark.pluck');
+
+  (function() {
+    var objects = {
+      'array': [{ '_': 'a' }, { '_': 'b' }, { '_': 'c' }, null],
+      'array-like-object': { '0': { '_': 'a' }, '1': { '_': 'b' }, '2': { '_': 'c' },  '3': null, 'length': 5 }
+    };
+
+    objects.array.length = 5;
+
+    forOwn(objects, function(object, key) {
+      test('produces the correct result when passing an ' + key, function() {
+        var actual = Benchmark.pluck(object, '_');
+        deepEqual(actual, ['a', 'b', 'c', undefined, undefined]);
+        equal('4' in actual, false);
+      });
+
+      test('produces the correct result for non-existent keys when passing an ' + key, function() {
+        var actual = Benchmark.pluck(object, 'non-existent');
+        deepEqual(actual, [undefined, undefined, undefined, undefined, undefined]);
+        equal('4' in actual, false);
+      });
+    });
+  }());
+
+  /*--------------------------------------------------------------------------*/
+
+  QUnit.module('Benchmark.reduce');
+
+  (function() {
+    var objects = {
+      'array': ['b', 'c', ''],
+      'array-like-object': { '0': 'b', '1': 'c',  '2': '', 'length': 4 }
+    };
+
+    objects.array.length = 4;
+
+    forOwn(objects, function(object, key) {
+      test('passes the correct arguments when passing an ' + key, function() {
+        var args;
+        Benchmark.reduce(object, function() {
+          args || (args = slice.call(arguments));
+        }, 'a');
+
+        deepEqual(args, ['a', 'b', 0, object]);
+      });
+
+      test('accumulates correctly when passing an ' + key, function() {
+        var actual = Benchmark.reduce(object, function(string, value) {
+          return string + value;
+        }, 'a');
+
+        equal(actual, 'abc');
+      });
+
+      test('handles arguments with no initial value correctly when passing an ' + key, function() {
+        var args;
+        Benchmark.reduce(object, function() {
+          args || (args = slice.call(arguments));
+        });
+
+        deepEqual(args, ['b', 'c', 1, object]);
+      });
+    });
+  }());
+
+  /*--------------------------------------------------------------------------*/
+
+  QUnit.module('Benchmark#clone');
+
+  (function() {
+    var bench = Benchmark(function() { this.count += 0; }).run();
+
+    test('produces the correct result passing no arguments', function() {
+      var clone = bench.clone();
+      deepEqual(clone, bench);
+      ok(clone.stats != bench.stats && clone.times != bench.times && clone.options != bench.options);
+    });
+
+    test('produces the correct result passing a data object', function() {
+      var clone = bench.clone({ 'fn': '', 'name': 'foo' });
+      ok(clone.fn === '' && clone.options.fn === '');
+      ok(clone.name == 'foo' && clone.options.name == 'foo');
+    });
+  }());
+
+  /*--------------------------------------------------------------------------*/
+
+  QUnit.module('Benchmark#run');
+
+  (function() {
+    var data = { 'onComplete': 0, 'onCycle': 0, 'onStart': 0 };
+
+    var bench = Benchmark({
+      'fn': function() {
+        this.count += 0;
+      },
+      'onStart': function() {
+        data.onStart++;
+      },
+      'onComplete': function() {
+        data.onComplete++;
+      }
+    })
+    .run();
+
+    test('onXYZ callbacks should not be triggered by internal benchmark clones', function() {
+      equal(data.onStart, 1);
+      equal(data.onComplete, 1);
+    });
+  }());
+
+  /*--------------------------------------------------------------------------*/
+
+  forOwn({
+    'Benchmark': Benchmark,
+    'Benchmark.Suite': Benchmark.Suite
+  },
+  function(Constructor, namespace) {
+
+    QUnit.module(namespace + '#emit');
+
+    (function() {
+      test('emits passed arguments', function() {
+        var args,
+            object = Constructor();
+
+        object.on('args', function() { args = slice.call(arguments, 1); });
+        object.emit('args', 'a', 'b', 'c');
+        deepEqual(args, ['a', 'b', 'c']);
+      });
+
+      test('emits with no listeners', function() {
+        var event = Benchmark.Event('empty'),
+            object = Constructor();
+
+        object.emit(event);
+        equal(event.cancelled, false);
+      });
+
+      test('emits with an event type of "toString"', function() {
+        var event = Benchmark.Event('toString'),
+            object = Constructor();
+
+        object.emit(event);
+        equal(event.cancelled, false);
+      });
+
+      test('returns the last listeners returned value', function() {
+        var event = Benchmark.Event('result'),
+            object = Constructor();
+
+        object.on('result', function() { return 'x'; });
+        object.on('result', function() { return 'y'; });
+        equal(object.emit(event), 'y');
+      });
+
+      test('aborts the emitters listener iteration when `event.aborted` is `true`', function() {
+        var event = Benchmark.Event('aborted'),
+            object = Constructor();
+
+        object.on('aborted', function(event) {
+          event.aborted = true;
+          return false;
+        });
+
+        object.on('aborted', function(event) {
+          // should not get here
+          event.aborted = false;
+          return true;
+        });
+
+        equal(object.emit(event), false);
+        equal(event.aborted, true);
+      });
+
+      test('cancels the event if a listener explicitly returns `false`', function() {
+        var event = Benchmark.Event('cancel'),
+            object = Constructor();
+
+        object.on('cancel', function() { return false; });
+        object.on('cancel', function() { return true; });
+        object.emit(event);
+        equal(event.cancelled, true);
+      });
+
+      test('uses a shallow clone of the listeners when emitting', function() {
+        var event,
+            listener2 = function(eventObject) { eventObject.listener2 = true },
+            object = Constructor();
+
+        object.on('shallowclone', function(eventObject) {
+          event = eventObject;
+          object.off(event.type, listener2);
+        })
+        .on('shallowclone', listener2)
+        .emit('shallowclone');
+
+        ok(event.listener2);
+      });
+
+      test('emits a custom event object', function() {
+        var event = Benchmark.Event('custom'),
+            object = Constructor();
+
+        object.on('custom', function(eventObject) { eventObject.touched = true; });
+        object.emit(event);
+        ok(event.touched);
+      });
+
+      test('sets `event.result` correctly', function() {
+        var event = Benchmark.Event('result'),
+            object = Constructor();
+
+        object.on('result', function() { return 'x'; });
+        object.emit(event);
+        equal(event.result, 'x');
+      });
+
+      test('sets `event.type` correctly', function() {
+        var event,
+            object = Constructor();
+
+        object.on('type', function(eventObj) {
+          event = eventObj;
+        });
+
+        object.emit('type');
+        equal(event.type, 'type');
+      });
+    }());
+
+    /*------------------------------------------------------------------------*/
+
+    QUnit.module(namespace + '#listeners');
+
+    (function() {
+      test('returns the correct listeners', function() {
+        var listener = function() { },
+            object = Constructor();
+
+        object.on('x', listener);
+        deepEqual(object.listeners('x'), [listener]);
+      });
+
+      test('returns an array and initializes previously uninitialized listeners', function() {
+        var object = Constructor();
+        deepEqual(object.listeners('x'), []);
+        deepEqual(object.events, { 'x': [] });
+      });
+    }());
+
+    /*------------------------------------------------------------------------*/
+
+    QUnit.module(namespace + '#off');
+
+    (function() {
+      test('returns the benchmark', function() {
+        var listener = function() { },
+            object = Constructor();
+
+        object.on('x', listener);
+        equal(object.off('x', listener), object);
+      });
+
+      test('will ignore inherited properties of the event cache', function() {
+        var Dummy = function() { },
+            listener = function() { },
+            object = Constructor();
+
+        Dummy.prototype.x = [listener];
+        object.events = new Dummy;
+
+        object.off('x', listener);
+        deepEqual(object.events.x, [listener]);
+      });
+
+      test('handles an event type and listener', function() {
+        var listener = function() { },
+            object = Constructor();
+
+        object.on('x', listener);
+        object.off('x', listener);
+        deepEqual(object.events.x, []);
+      });
+
+      test('handles unregistering duplicate listeners', function() {
+        var listener = function() { },
+            object = Constructor();
+
+        object.on('x', listener);
+        object.on('x', listener);
+
+        var events = object.events;
+        object.off('x', listener);
+        deepEqual(events.x, [listener]);
+
+        object.off('x', listener);
+        deepEqual(events.x, []);
+      });
+
+      test('handles a non-registered listener', function() {
+        var object = Constructor();
+        object.off('x', function() { });
+        equal(object.events, undefined);
+      });
+
+      test('handles space separated event type and listener', function() {
+        var listener = function() { },
+            object = Constructor();
+
+        object.on('x', listener);
+        object.on('y', listener);
+
+        var events = object.events;
+        object.off('x y', listener);
+        deepEqual(events.x, []);
+        deepEqual(events.y, []);
+      });
+
+      test('handles space separated event type and no listener', function() {
+        var listener1 = function() { },
+            listener2 = function() { },
+            object = Constructor();
+
+        object.on('x', listener1);
+        object.on('y', listener2);
+
+        var events = object.events;
+        object.off('x y');
+        deepEqual(events.x, []);
+        deepEqual(events.y, []);
+      });
+
+      test('handles no arguments', function() {
+        var listener1 = function() { },
+            listener2 = function() { },
+            listener3 = function() { },
+            object = Constructor();
+
+        object.on('x', listener1);
+        object.on('y', listener2);
+        object.on('z', listener3);
+
+        var events = object.events;
+        object.off();
+        deepEqual(events.x, []);
+        deepEqual(events.y, []);
+        deepEqual(events.z, []);
+      });
+    }());
+
+    /*------------------------------------------------------------------------*/
+
+    QUnit.module(namespace + '#on');
+
+    (function() {
+      test('returns the benchmark', function() {
+        var listener = function() { },
+            object = Constructor();
+
+        equal(object.on('x', listener), object);
+      });
+
+      test('will ignore inherited properties of the event cache', function() {
+        var Dummy = function() { },
+            listener1 = function() { },
+            listener2 = function() { },
+            object = Constructor();
+
+        Dummy.prototype.x = [listener1];
+        object.events = new Dummy;
+
+        object.on('x', listener2);
+        deepEqual(object.events.x, [listener2]);
+      });
+
+      test('handles an event type and listener', function() {
+        var listener = function() { },
+            object = Constructor();
+
+        object.on('x', listener);
+        deepEqual(object.events.x, [listener]);
+      });
+
+      test('handles registering duplicate listeners', function() {
+        var listener = function() { },
+            object = Constructor();
+
+        object.on('x', listener);
+        object.on('x', listener);
+        deepEqual(object.events.x, [listener, listener]);
+      });
+
+      test('handles space separated event type and listener', function() {
+        var listener = function() { },
+            object = Constructor();
+
+        object.on('x y', listener);
+
+        var events = object.events;
+        deepEqual(events.x, [listener]);
+        deepEqual(events.y, [listener]);
+      });
+    }());
+  });
+
+  /*--------------------------------------------------------------------------*/
+
+  QUnit.module('Benchmark.Suite#abort');
+
+  (function() {
+    test('igores abort calls when the suite isn\'t running', function() {
+      var fired = false;
+      var suite = Benchmark.Suite('suite', {
+        'onAbort': function() { fired = true; }
+      });
+
+      suite.add('foo', function() { });
+      suite.abort();
+      equal(fired, false);
+    });
+
+    test('ignores abort calls from `Benchmark.Suite#reset` when the suite isn\'t running', function() {
+      var fired = false;
+      var suite = Benchmark.Suite('suite', {
+        'onAbort': function() { fired = true; }
+      });
+
+      suite.add('foo', function() { });
+      suite.reset();
+      equal(fired, false);
+    });
+
+    asyncTest('emits an abort event when running', function() {
+      var fired = false;
+
+      Benchmark.Suite({
+        'onAbort': function() { fired = true; }
+      })
+      .on('start', function() {
+        this.abort();
+      })
+      .on('complete', function() {
+        ok(fired);
+        QUnit.start();
+      })
+      .add(function(){ })
+      .run({ 'async': true });
+    });
+
+    asyncTest('emits an abort event after calling `Benchmark.Suite#reset`', function() {
+      var fired = false;
+
+      Benchmark.Suite({
+        'onAbort': function() { fired = true; }
+      })
+      .on('start', function() {
+        this.reset();
+      })
+      .on('complete', function() {
+        ok(fired);
+        QUnit.start();
+      })
+      .add(function(){ })
+      .run({ 'async': true });
+    });
+
+    asyncTest('should abort deferred benchmark', function() {
+      var fired = false,
+          suite = Benchmark.Suite();
+
+      suite.on('complete', function() {
+        equal(fired, false);
+        QUnit.start();
+      })
+      .add('a', {
+        'defer': true,
+        'fn': function(deferred) {
+          // avoid test inlining
+          suite.name;
+          // delay resolve
+          setTimeout(function() {
+            deferred.resolve();
+            suite.abort();
+          }, 10);
+        }
+      })
+      .add('b', {
+        'defer': true,
+        'fn': function(deferred) {
+          // avoid test inlining
+          suite.name;
+          // delay resolve
+          setTimeout(function() {
+            deferred.resolve();
+            fired = true;
+          }, 10);
+        }
+      })
+      .run();
+    });
+  }());
+
+  /*--------------------------------------------------------------------------*/
+
+  QUnit.module('Benchmark.Suite#concat');
+
+  (function() {
+    var args = arguments;
+
+    test('doesn\'t treat an arguments object like an array', function() {
+      var suite = Benchmark.Suite();
+      deepEqual(suite.concat(args), [args]);
+    });
+
+    test('flattens array arguments', function() {
+      var suite = Benchmark.Suite();
+      deepEqual(suite.concat([1, 2], 3, [4, 5]), [1, 2, 3, 4, 5]);
+    });
+
+    test('supports concating sparse arrays', function() {
+      var suite = Benchmark.Suite();
+      suite[0] = 0;
+      suite[2] = 2;
+      suite.length = 3;
+
+      var actual = suite.concat(3);
+      deepEqual(actual, [0, undefined, 2, 3]);
+      equal('1' in actual, false);
+    });
+
+    test('supports sparse arrays as arguments', function() {
+      var suite = Benchmark.Suite(),
+          sparse = [];
+
+      sparse[0] = 0;
+      sparse[2] = 2;
+      sparse.length = 3;
+
+      var actual = suite.concat(sparse);
+      deepEqual(actual, [0, undefined, 2]);
+      equal('1' in actual, false);
+    });
+
+    test('creates a new array', function() {
+      var suite = Benchmark.Suite();
+      ok(suite.concat(1) !== suite);
+    });
+  }(1, 2, 3));
+
+  /*--------------------------------------------------------------------------*/
+
+  QUnit.module('Benchmark.Suite#reverse');
+
+  (function() {
+    test('reverses the element order', function() {
+      var suite = Benchmark.Suite();
+      suite[0] = 0;
+      suite[1] = 1;
+      suite.length = 2;
+
+      var actual = suite.reverse();
+      equal(actual, suite);
+      deepEqual(slice.call(actual), [1, 0]);
+    });
+
+    test('supports reversing sparse arrays', function() {
+      var suite = Benchmark.Suite();
+      suite[0] = 0;
+      suite[2] = 2;
+      suite.length = 3;
+
+      var actual = suite.reverse();
+      equal(actual, suite);
+      deepEqual(slice.call(actual), [2, undefined, 0]);
+      equal('1' in actual, false);
+    });
+  }());
+
+  /*--------------------------------------------------------------------------*/
+
+  QUnit.module('Benchmark.Suite#shift');
+
+  (function() {
+    test('removes the first element', function() {
+      var suite = Benchmark.Suite();
+      suite[0] = 0;
+      suite[1] = 1;
+      suite.length = 2;
+
+      var actual = suite.shift();
+      equal(actual, 0);
+      deepEqual(slice.call(suite), [1]);
+    });
+
+    test('shifts an object with no elements', function() {
+      var suite = Benchmark.Suite(),
+          actual = suite.shift();
+
+      equal(actual, undefined);
+      deepEqual(slice.call(suite), []);
+    });
+
+    test('should have no elements when length is 0 after shift', function() {
+      var suite = Benchmark.Suite();
+      suite[0] = 0;
+      suite.length = 1;
+      suite.shift();
+
+      // ensure element is removed
+      equal('0' in suite, false);
+      equal(suite.length, 0);
+    });
+
+    test('supports shifting sparse arrays', function() {
+      var suite = Benchmark.Suite();
+      suite[1] = 1;
+      suite[3] = 3;
+      suite.length = 4;
+
+      var actual = suite.shift();
+      equal(actual, undefined);
+      deepEqual(slice.call(suite), [1, undefined, 3]);
+      equal('1' in suite, false);
+    });
+  }());
+
+  /*--------------------------------------------------------------------------*/
+
+  QUnit.module('Benchmark.Suite#slice');
+
+  (function() {
+    var suite = Benchmark.Suite();
+    suite[0] = 0;
+    suite[1] = 1;
+    suite[2] = 2;
+    suite[3] = 3;
+    suite.length = 4;
+
+    test('works with no arguments', function() {
+      var actual = suite.slice();
+      deepEqual(actual, [0, 1, 2, 3]);
+      ok(suite !== actual);
+    });
+
+    test('works with positive `start` argument', function() {
+      var actual = suite.slice(2);
+      deepEqual(actual, [2, 3]);
+      ok(suite !== actual);
+    });
+
+    test('works with positive `start` and `end` arguments', function() {
+      var actual = suite.slice(1, 3);
+      deepEqual(actual, [1, 2]);
+      ok(suite !== actual);
+    });
+
+    test('works with `end` values exceeding length', function() {
+      var actual = suite.slice(1, 10);
+      deepEqual(actual, [1, 2, 3]);
+      ok(suite !== actual);
+    });
+
+    test('works with negative `start` and `end` arguments', function() {
+      var actual = suite.slice(-3, -1);
+      deepEqual(actual, [1, 2]);
+      ok(suite !== actual);
+    });
+
+    test('works with an extreme negative `end` value', function() {
+      var actual = suite.slice(1, -10);
+      deepEqual(actual, []);
+      equal('-1' in actual, false);
+      ok(suite !== actual);
+    });
+
+    test('supports slicing sparse arrays', function() {
+      var sparse = Benchmark.Suite();
+      sparse[1] = 1;
+      sparse[3] = 3;
+      sparse.length = 4;
+
+      var actual = sparse.slice(0, 2);
+      deepEqual(actual, [undefined, 1]);
+      equal('0' in actual, false);
+
+      actual = sparse.slice(1);
+      deepEqual(actual, [1, undefined, 3]);
+      equal('1' in actual, false);
+    });
+  }());
+
+  /*--------------------------------------------------------------------------*/
+
+  QUnit.module('Benchmark.Suite#splice');
+
+  (function() {
+    test('works with no arguments', function() {
+      var suite = Benchmark.Suite();
+      suite[0] = 0;
+      suite.length = 1;
+
+      var actual = suite.splice();
+      deepEqual(actual, []);
+      deepEqual(slice.call(suite), [0]);
+    });
+
+    test('works with only the `start` argument', function() {
+      var suite = Benchmark.Suite();
+      suite[0] = 0;
+      suite[1] = 1;
+      suite.length = 2;
+
+      var actual = suite.splice(1);
+      deepEqual(actual, [1]);
+      deepEqual(slice.call(suite), [0]);
+    });
+
+    test('should have no elements when length is 0 after splice', function() {
+      var suite = Benchmark.Suite();
+      suite[0] = 0;
+      suite.length = 1
+      suite.splice(0, 1);
+
+      // ensure element is removed
+      equal('0' in suite, false);
+      equal(suite.length, 0);
+    });
+
+    test('works with positive `start` argument', function() {
+      var suite = Benchmark.Suite();
+      suite[0] = 0;
+      suite[1] = 3;
+      suite.length = 2;
+
+      var actual = suite.splice(1, 0, 1, 2);
+      deepEqual(actual, []);
+      deepEqual(slice.call(suite), [0, 1, 2, 3]);
+    });
+
+    test('works with positive `start` and `deleteCount` arguments', function() {
+      var suite = Benchmark.Suite();
+      suite[0] = 0;
+      suite[1] = 3;
+      suite.length = 2;
+
+      var actual = suite.splice(1, 1, 1, 2);
+      deepEqual(actual, [3]);
+      deepEqual(slice.call(suite), [0, 1, 2]);
+    });
+
+    test('works with `deleteCount` values exceeding length', function() {
+      var suite = Benchmark.Suite();
+      suite[0] = 0;
+      suite[1] = 3;
+      suite.length = 2;
+
+      var actual = suite.splice(1, 10, 1, 2);
+      deepEqual(actual, [3]);
+      deepEqual(slice.call(suite), [0, 1, 2]);
+    });
+
+    test('works with negative `start` and `deleteCount` arguments', function() {
+      var suite = Benchmark.Suite();
+      suite[0] = 0;
+      suite[1] = 3;
+      suite.length = 2;
+
+      var actual = suite.splice(-1, -1, 1, 2);
+      deepEqual(actual, []);
+      deepEqual(slice.call(suite), [0, 1, 2, 3]);
+    });
+
+    test('works with an extreme negative `deleteCount` value', function() {
+      var suite = Benchmark.Suite();
+      suite[0] = 0;
+      suite[1] = 3;
+      suite.length = 2;
+
+      var actual = suite.splice(0, -10, 1, 2);
+      deepEqual(actual, []);
+      deepEqual(slice.call(suite), [1, 2, 0, 3]);
+    });
+
+    test('supports splicing sparse arrays', function() {
+      var suite = Benchmark.Suite();
+      suite[1] = 1;
+      suite[3] = 3;
+      suite.length = 4;
+
+      var actual = suite.splice(1, 2, 1, 2);
+      deepEqual(actual, [1, undefined]);
+      equal(actual.length, 2);
+      deepEqual(slice.call(suite), [undefined, 1, 2, 3]);
+      equal('0' in suite, false);
+    });
+  }());
+
+  /*--------------------------------------------------------------------------*/
+
+  QUnit.module('Benchmark.Suite#unshift');
+
+  (function() {
+    test('adds a first element', function() {
+      var suite = Benchmark.Suite();
+      suite[0] = 1;
+      suite.length = 1;
+
+      var actual = suite.unshift(0);
+      equal(actual, 2);
+      deepEqual(slice.call(suite), [0, 1]);
+    });
+
+    test('adds multiple elements to the front', function() {
+      var suite = Benchmark.Suite();
+      suite[0] = 3;
+      suite.length = 1;
+
+      var actual = suite.unshift(0, 1, 2);
+      equal(actual, 4);
+      deepEqual(slice.call(suite), [0, 1, 2, 3]);
+    });
+
+    test('supports unshifting sparse arrays', function() {
+      var suite = Benchmark.Suite();
+      suite[1] = 2;
+      suite.length = 2;
+
+      var actual = suite.unshift(0);
+      equal(actual, 3);
+      deepEqual(slice.call(suite), [0, undefined, 2]);
+      equal('1' in suite, false);
+    });
+  }());
+
+  /*--------------------------------------------------------------------------*/
+
+  QUnit.module('Benchmark.Suite filtered results onComplete');
+
+  (function() {
+    var count = 0,
+        suite = Benchmark.Suite();
+
+    suite.add('a', function() {
+      count++;
+    })
+    .add('b', function() {
+      for (var i = 0; i < 1e6; i++) {
+        count++;
+      }
+    })
+    .add('c', function() {
+      throw new TypeError;
+    });
+
+    asyncTest('should filter by fastest', function() {
+      suite.on('complete', function() {
+        suite.off();
+        deepEqual(this.filter('fastest').pluck('name'), ['a']);
+        QUnit.start();
+      })
+      .run({ 'async': true });
+    });
+
+    asyncTest('should filter by slowest', function() {
+      suite.on('complete', function() {
+        suite.off();
+        deepEqual(this.filter('slowest').pluck('name'), ['b']);
+        QUnit.start();
+      })
+      .run({ 'async': true });
+    });
+
+    asyncTest('should filter by successful', function() {
+      suite.on('complete', function() {
+        suite.off();
+        deepEqual(this.filter('successful').pluck('name'), ['a', 'b']);
+        QUnit.start();
+      })
+      .run({ 'async': true });
+    });
+  }());
+
+  /*--------------------------------------------------------------------------*/
+
+  QUnit.module('Benchmark.Suite event flow');
+
+  (function() {
+    var events = [],
+        callback = function(event) { events.push(event); };
+
+    var suite = Benchmark.Suite('suite', {
+      'onAdd': callback,
+      'onAbort': callback,
+      'onClone': callback,
+      'onError': callback,
+      'onStart': callback,
+      'onCycle': callback,
+      'onComplete': callback,
+      'onReset': callback
+    })
+    .add('bench', function() {
+      throw null;
+    }, {
+      'onAbort': callback,
+      'onClone': callback,
+      'onError': callback,
+      'onStart': callback,
+      'onCycle': callback,
+      'onComplete': callback,
+      'onReset': callback
+    })
+    .run({ 'async': false });
+
+    // first Suite#onAdd
+    test('should emit the suite "add" event first', function() {
+      var event = events[0];
+      ok(event.type == 'add' && event.currentTarget.name == 'suite' && event.target.name == 'bench');
+    });
+
+    // next we start the Suite because no reset was needed
+    test('should emit the suite "start" event', function() {
+      var event = events[1];
+      ok(event.type == 'start' && event.currentTarget.name == 'suite' && event.target.name == 'bench');
+    });
+
+    // and so start the first benchmark
+    test('should emit the benchmark "start" event', function() {
+      var event = events[2];
+      ok(event.type == 'start' && event.currentTarget.name == 'bench');
+    });
+
+    // oh no! we abort because of an error
+    test('should emit the benchmark "error" event', function() {
+      var event = events[3];
+      ok(event.type == 'error' && event.currentTarget.name == 'bench');
+    });
+
+    // benchmark error triggered
+    test('should emit the benchmark "abort" event', function() {
+      var event = events[4];
+      ok(event.type == 'abort' && event.currentTarget.name == 'bench');
+    });
+
+    // we reset the benchmark as part of the abort
+    test('should emit the benchmark "reset" event', function() {
+      var event = events[5];
+      ok(event.type == 'reset' && event.currentTarget.name == 'bench');
+    });
+
+    // benchmark is cycle is finished
+    test('should emit the benchmark "cycle" event', function() {
+      var event = events[6];
+      ok(event.type == 'cycle' && event.currentTarget.name == 'bench');
+    });
+
+    // benchmark is complete
+    test('should emit the benchmark "complete" event', function() {
+      var event = events[7];
+      ok(event.type == 'complete' && event.currentTarget.name == 'bench');
+    });
+
+    // the benchmark error triggers a Suite error
+    test('should emit the suite "error" event', function() {
+      var event = events[8];
+      ok(event.type == 'error' && event.currentTarget.name == 'suite' && event.target.name == 'bench');
+    });
+
+    // the Suite cycle finishes
+    test('should emit the suite "cycle" event', function() {
+      var event = events[9];
+      ok(event.type == 'cycle' && event.currentTarget.name == 'suite' && event.target.name == 'bench');
+    });
+
+    // the Suite completes
+    test('finally it should emit the suite "complete" event', function() {
+      var event = events[10];
+      ok(event.type == 'complete' && event.currentTarget.name == 'suite' && event.target.name == 'bench');
+    });
+
+    test('emitted all expected events', function() {
+      ok(events.length == 11);
+    });
+  }());
+
+  /*--------------------------------------------------------------------------*/
+
+  QUnit.module('Deferred benchmarks');
+
+  (function() {
+    asyncTest('should run a deferred benchmark correctly', function() {
+      Benchmark(function(deferred) {
+        setTimeout(function() { deferred.resolve(); }, 1e3);
+      }, {
+        'defer': true,
+        'onComplete': function() {
+          equal(this.hz.toFixed(0), 1);
+          QUnit.start();
+        }
+      })
+      .run();
+    });
+
+    asyncTest('should run with string values for "fn", "setup", and "teardown"', function() {
+      Benchmark({
+        'defer': true,
+        'setup': 'var x = [3, 2, 1];',
+        'fn': 'setTimeout(function() { x.sort(); deferred.resolve(); }, 10);',
+        'teardown': 'x.length = 0;',
+        'onComplete': function() {
+          ok(true);
+          QUnit.start();
+        }
+      })
+      .run();
+    });
+
+    asyncTest('should run recursively', function() {
+      Benchmark({
+        'defer': true,
+        'setup': 'var x = [3, 2, 1];',
+        'fn': 'for (var i = 0; i < 100; i++) x[ i % 2 ? "sort" : "reverse" ](); deferred.resolve();',
+        'teardown': 'x.length = 0;',
+        'onComplete': function() {
+          ok(true);
+          QUnit.start();
+        }
+      })
+      .run();
+    });
+
+    asyncTest('should execute "setup", "fn", and "teardown" in correct order', function() {
+      var fired = [];
+
+      Benchmark({
+        'defer': true,
+        'setup': function() {
+          fired.push('setup');
+        },
+        'fn': function(deferred) {
+          fired.push('fn');
+          setTimeout(function() { deferred.resolve(); }, 10);
+        },
+        'teardown': function() {
+          fired.push('teardown');
+        },
+        'onComplete': function() {
+          var actual = fired.join().replace(/(fn,)+/g, '$1').replace(/(setup,fn,teardown(?:,|$))+/, '$1');
+          equal(actual, 'setup,fn,teardown');
+          QUnit.start();
+        }
+      })
+      .run();
+    });
+  }());
+
+  /*--------------------------------------------------------------------------*/
+
+  QUnit.module('Benchmark.deepClone');
+
+  (function() {
+    asyncTest('avoids call stack limits', function() {
+      var result,
+          count = 0,
+          object = {},
+          recurse = function() { count++; recurse(); };
+
+      setTimeout(function() {
+        ok(result, 'avoids call stack limits (stack limit is ' + (count - 1) + ')');
+        QUnit.start();
+      }, 15);
+
+      if (toString.call(window.java) == '[object JavaPackage]') {
+        // Java throws uncatchable errors on call stack overflows, so to avoid
+        // them I chose a number higher than Rhino's call stack limit without
+        // dynamically testing for the actual limit
+        count = 3e3;
+      } else {
+        try { recurse(); } catch(e) { }
+      }
+
+      // exceed limit
+      count++;
+      for (var i = 0, sub = object; i <= count; i++) {
+        sub = sub[i] = {};
+      }
+
+      try {
+        for (var i = 0, sub = Benchmark.deepClone(object); sub = sub[i]; i++) { }
+        result = --i == count;
+      } catch(e) { }
+    });
+  }());
+
+  /*--------------------------------------------------------------------------*/
+
+  // explicitly call `QUnit.start()` for Narwhal, Rhino, and RingoJS
+  if (!window.document) {
+    QUnit.start();
+  }
+}(typeof global == 'object' && global || this));
diff --git a/cep/node_modules/better-assert/.npmignore b/cep/node_modules/better-assert/.npmignore
new file mode 100644
index 0000000..f1250e5
--- /dev/null
+++ b/cep/node_modules/better-assert/.npmignore
@@ -0,0 +1,4 @@
+support
+test
+examples
+*.sock
diff --git a/cep/node_modules/better-assert/History.md b/cep/node_modules/better-assert/History.md
new file mode 100644
index 0000000..cbb579b
--- /dev/null
+++ b/cep/node_modules/better-assert/History.md
@@ -0,0 +1,15 @@
+
+1.0.0 / 2013-02-03 
+==================
+
+  * Stop using the removed magic __stack global getter
+
+0.1.0 / 2012-10-04 
+==================
+
+  * add throwing of AssertionError for test frameworks etc
+
+0.0.1 / 2010-01-03
+==================
+
+  * Initial release
diff --git a/cep/node_modules/better-assert/Makefile b/cep/node_modules/better-assert/Makefile
new file mode 100644
index 0000000..36a3ed7
--- /dev/null
+++ b/cep/node_modules/better-assert/Makefile
@@ -0,0 +1,5 @@
+
+test:
+	@echo "populate me"
+
+.PHONY: test
\ No newline at end of file
diff --git a/cep/node_modules/better-assert/Readme.md b/cep/node_modules/better-assert/Readme.md
new file mode 100644
index 0000000..d8d3a63
--- /dev/null
+++ b/cep/node_modules/better-assert/Readme.md
@@ -0,0 +1,61 @@
+
+# better-assert
+
+  Better c-style assertions using [callsite](https://github.com/visionmedia/callsite) for
+  self-documenting failure messages.
+
+## Installation
+
+    $ npm install better-assert
+
+## Example
+
+ By default assertions are enabled, however the __NO_ASSERT__ environment variable 
+ will deactivate them when truthy.
+
+```js
+var assert = require('better-assert');
+
+test();
+
+function test() {
+  var user = { name: 'tobi' };
+  assert('tobi' == user.name);
+  assert('number' == typeof user.age);
+}
+
+AssertionError: 'number' == typeof user.age
+    at test (/Users/tj/projects/better-assert/example.js:9:3)
+    at Object. (/Users/tj/projects/better-assert/example.js:4:1)
+    at Module._compile (module.js:449:26)
+    at Object.Module._extensions..js (module.js:467:10)
+    at Module.load (module.js:356:32)
+    at Function.Module._load (module.js:312:12)
+    at Module.runMain (module.js:492:10)
+    at process.startup.processNextTick.process._tickCallback (node.js:244:9)
+```
+
+## License 
+
+(The MIT License)
+
+Copyright (c) 2012 TJ Holowaychuk <tj@vision-media.ca>
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\ No newline at end of file
diff --git a/cep/node_modules/better-assert/example.js b/cep/node_modules/better-assert/example.js
new file mode 100644
index 0000000..688c29e
--- /dev/null
+++ b/cep/node_modules/better-assert/example.js
@@ -0,0 +1,10 @@
+
+var assert = require('./');
+
+test();
+
+function test() {
+  var user = { name: 'tobi' };
+  assert('tobi' == user.name);
+  assert('number' == typeof user.age);
+}
\ No newline at end of file
diff --git a/cep/node_modules/better-assert/index.js b/cep/node_modules/better-assert/index.js
new file mode 100644
index 0000000..fd1c9b7
--- /dev/null
+++ b/cep/node_modules/better-assert/index.js
@@ -0,0 +1,38 @@
+/**
+ * Module dependencies.
+ */
+
+var AssertionError = require('assert').AssertionError
+  , callsite = require('callsite')
+  , fs = require('fs')
+
+/**
+ * Expose `assert`.
+ */
+
+module.exports = process.env.NO_ASSERT
+  ? function(){}
+  : assert;
+
+/**
+ * Assert the given `expr`.
+ */
+
+function assert(expr) {
+  if (expr) return;
+
+  var stack = callsite();
+  var call = stack[1];
+  var file = call.getFileName();
+  var lineno = call.getLineNumber();
+  var src = fs.readFileSync(file, 'utf8');
+  var line = src.split('\n')[lineno-1];
+  var src = line.match(/assert\((.*)\)/)[1];
+
+  var err = new AssertionError({
+    message: src,
+    stackStartFunction: stack[0].getFunction()
+  });
+
+  throw err;
+}
diff --git a/cep/node_modules/better-assert/package.json b/cep/node_modules/better-assert/package.json
new file mode 100644
index 0000000..2339436
--- /dev/null
+++ b/cep/node_modules/better-assert/package.json
@@ -0,0 +1,101 @@
+{
+  "_args": [
+    [
+      {
+        "raw": "better-assert@~1.0.0",
+        "scope": null,
+        "escapedName": "better-assert",
+        "name": "better-assert",
+        "rawSpec": "~1.0.0",
+        "spec": ">=1.0.0 <1.1.0",
+        "type": "range"
+      },
+      "D:\\repositories\\tk-framework-adobe\\extensions\\http_server\\node_modules\\parsejson"
+    ]
+  ],
+  "_from": "better-assert@>=1.0.0 <1.1.0",
+  "_id": "better-assert@1.0.2",
+  "_inCache": true,
+  "_installable": true,
+  "_location": "/better-assert",
+  "_npmUser": {
+    "name": "tony_ado",
+    "email": "coolhzb@163.com"
+  },
+  "_npmVersion": "1.4.9",
+  "_phantomChildren": {},
+  "_requested": {
+    "raw": "better-assert@~1.0.0",
+    "scope": null,
+    "escapedName": "better-assert",
+    "name": "better-assert",
+    "rawSpec": "~1.0.0",
+    "spec": ">=1.0.0 <1.1.0",
+    "type": "range"
+  },
+  "_requiredBy": [
+    "/parsejson",
+    "/parseqs",
+    "/parseuri"
+  ],
+  "_resolved": "https://registry.npmjs.org/better-assert/-/better-assert-1.0.2.tgz",
+  "_shasum": "40866b9e1b9e0b55b481894311e68faffaebc522",
+  "_shrinkwrap": null,
+  "_spec": "better-assert@~1.0.0",
+  "_where": "D:\\repositories\\tk-framework-adobe\\extensions\\http_server\\node_modules\\parsejson",
+  "author": {
+    "name": "TJ Holowaychuk",
+    "email": "tj@vision-media.ca"
+  },
+  "bugs": {
+    "url": "https://github.com/visionmedia/better-assert/issues"
+  },
+  "contributors": [
+    {
+      "name": "TonyHe",
+      "email": "coolhzb@163.com"
+    },
+    {
+      "name": "ForbesLindesay"
+    }
+  ],
+  "dependencies": {
+    "callsite": "1.0.0"
+  },
+  "description": "Better assertions for node, reporting the expr, filename, lineno etc",
+  "devDependencies": {},
+  "directories": {},
+  "dist": {
+    "shasum": "40866b9e1b9e0b55b481894311e68faffaebc522",
+    "tarball": "https://registry.npmjs.org/better-assert/-/better-assert-1.0.2.tgz"
+  },
+  "engines": {
+    "node": "*"
+  },
+  "homepage": "https://github.com/visionmedia/better-assert",
+  "keywords": [
+    "assert",
+    "stack",
+    "trace",
+    "debug"
+  ],
+  "main": "index",
+  "maintainers": [
+    {
+      "name": "tjholowaychuk",
+      "email": "tj@vision-media.ca"
+    },
+    {
+      "name": "tony_ado",
+      "email": "coolhzb@163.com"
+    }
+  ],
+  "name": "better-assert",
+  "optionalDependencies": {},
+  "readme": "ERROR: No README data found!",
+  "repository": {
+    "type": "git",
+    "url": "git+https://github.com/visionmedia/better-assert.git"
+  },
+  "version": "1.0.2"
+}
diff --git a/cep/node_modules/blob/.npmignore b/cep/node_modules/blob/.npmignore
new file mode 100644
index 0000000..548a368
--- /dev/null
+++ b/cep/node_modules/blob/.npmignore
@@ -0,0 +1,2 @@
+node_modules
+blob.js
diff --git a/cep/node_modules/blob/.zuul.yml b/cep/node_modules/blob/.zuul.yml
new file mode 100644
index 0000000..380c395
--- /dev/null
+++ b/cep/node_modules/blob/.zuul.yml
@@ -0,0 +1,14 @@
+ui: mocha-bdd
+browsers:
+  - name: chrome
+    version: 8..latest
+  - name: firefox
+    version: 7..latest
+  - name: safari
+    version: 6..latest
+  - name: opera
+    version: 12.1..latest
+  - name: ie
+    version: 10..latest
+  - name: android
+    version: latest
diff --git a/cep/node_modules/blob/Makefile b/cep/node_modules/blob/Makefile
new file mode 100644
index 0000000..7d9601a
--- /dev/null
+++ b/cep/node_modules/blob/Makefile
@@ -0,0 +1,14 @@
+REPORTER = dot
+
+build: blob.js
+
+blob.js:
+	@./node_modules/.bin/browserify --standalone blob index.js > blob.js
+
+test:
+	@./node_modules/.bin/zuul -- test/index.js
+
+clean:
+	rm blob.js
+
+.PHONY: test blob.js
diff --git a/cep/node_modules/blob/README.md b/cep/node_modules/blob/README.md
new file mode 100644
index 0000000..6915955
--- /dev/null
+++ b/cep/node_modules/blob/README.md
@@ -0,0 +1,14 @@
+Blob
+====
+
+A module that exports a constructor that uses window.Blob when available, and a BlobBuilder with any vendor prefix in other cases. If neither is available, it exports undefined.
+
+Usage:
+
+```javascript
+var Blob = require('blob');
+var b = new Blob(['hi', 'constructing', 'a', 'blob']);
+```
+
+## Licence
+MIT
diff --git a/cep/node_modules/blob/index.js b/cep/node_modules/blob/index.js
new file mode 100644
index 0000000..cad3f84
--- /dev/null
+++ b/cep/node_modules/blob/index.js
@@ -0,0 +1,96 @@
+/**
+ * Create a blob builder even when vendor prefixes exist
+ */
+
+var BlobBuilder = global.BlobBuilder
+  || global.WebKitBlobBuilder
+  || global.MSBlobBuilder
+  || global.MozBlobBuilder;
+
+/**
+ * Check if Blob constructor is supported
+ */
+
+var blobSupported = (function() {
+  try {
+    var a = new Blob(['hi']);
+    return a.size === 2;
+  } catch(e) {
+    return false;
+  }
+})();
+
+/**
+ * Check if Blob constructor supports ArrayBufferViews
+ * Fails in Safari 6, so we need to map to ArrayBuffers there.
+ */
+
+var blobSupportsArrayBufferView = blobSupported && (function() {
+  try {
+    var b = new Blob([new Uint8Array([1,2])]);
+    return b.size === 2;
+  } catch(e) {
+    return false;
+  }
+})();
+
+/**
+ * Check if BlobBuilder is supported
+ */
+
+var blobBuilderSupported = BlobBuilder
+  && BlobBuilder.prototype.append
+  && BlobBuilder.prototype.getBlob;
+
+/**
+ * Helper function that maps ArrayBufferViews to ArrayBuffers
+ * Used by BlobBuilder constructor and old browsers that didn't
+ * support it in the Blob constructor.
+ */
+
+function mapArrayBufferViews(ary) {
+  for (var i = 0; i < ary.length; i++) {
+    var chunk = ary[i];
+    if (chunk.buffer instanceof ArrayBuffer) {
+      var buf = chunk.buffer;
+
+      // if this is a subarray, make a copy so we only
+      // include the subarray region from the underlying buffer
+      if (chunk.byteLength !== buf.byteLength) {
+        var copy = new Uint8Array(chunk.byteLength);
+        copy.set(new Uint8Array(buf, chunk.byteOffset, chunk.byteLength));
+        buf = copy.buffer;
+      }
+
+      ary[i] = buf;
+    }
+  }
+}
+
+function BlobBuilderConstructor(ary, options) {
+  options = options || {};
+
+  var bb = new BlobBuilder();
+  mapArrayBufferViews(ary);
+
+  for (var i = 0; i < ary.length; i++) {
+    bb.append(ary[i]);
+  }
+
+  return (options.type) ? bb.getBlob(options.type) : bb.getBlob();
+};
+
+function BlobConstructor(ary, options) {
+  mapArrayBufferViews(ary);
+  return new Blob(ary, options || {});
+};
+
+module.exports = (function() {
+  if (blobSupported) {
+    return blobSupportsArrayBufferView ? global.Blob : BlobConstructor;
+  } else if (blobBuilderSupported) {
+    return BlobBuilderConstructor;
+  } else {
+    return undefined;
+  }
+})();
diff --git a/cep/node_modules/blob/package.json b/cep/node_modules/blob/package.json
new file mode 100644
index 0000000..a47c3e2
--- /dev/null
+++ b/cep/node_modules/blob/package.json
@@ -0,0 +1,78 @@
+{
+  "_args": [
+    [
+      {
+        "raw": "blob@0.0.4",
+        "scope": null,
+        "escapedName": "blob",
+        "name": "blob",
+        "rawSpec": "0.0.4",
+        "spec": "0.0.4",
+        "type": "version"
+      },
+      "D:\\repositories\\tk-framework-adobe\\extensions\\http_server\\node_modules\\engine.io-parser"
+    ]
+  ],
+  "_from": "blob@0.0.4",
+  "_id": "blob@0.0.4",
+  "_inCache": true,
+  "_installable": true,
+  "_location": "/blob",
+  "_npmUser": {
+    "name": "rase-",
+    "email": "tonykovanen@hotmail.com"
+  },
+  "_npmVersion": "1.4.6",
+  "_phantomChildren": {},
+  "_requested": {
+    "raw": "blob@0.0.4",
+    "scope": null,
+    "escapedName": "blob",
+    "name": "blob",
+    "rawSpec": "0.0.4",
+    "spec": "0.0.4",
+    "type": "version"
+  },
+  "_requiredBy": [
+    "/engine.io-parser"
+  ],
+  "_resolved": "https://registry.npmjs.org/blob/-/blob-0.0.4.tgz",
+  "_shasum": "bcf13052ca54463f30f9fc7e95b9a47630a94921",
+  "_shrinkwrap": null,
+  "_spec": "blob@0.0.4",
+  "_where": "D:\\repositories\\tk-framework-adobe\\extensions\\http_server\\node_modules\\engine.io-parser",
+  "bugs": {
+    "url": "https://github.com/rase-/blob/issues"
+  },
+  "dependencies": {},
+  "description": "Abstracts out Blob and uses BlobBulder in cases where it is supported with any vendor prefix.",
+  "devDependencies": {
+    "browserify": "3.30.1",
+    "expect.js": "0.2.0",
+    "mocha": "1.17.1",
+    "zuul": "1.5.4"
+  },
+  "directories": {},
+  "dist": {
+    "shasum": "bcf13052ca54463f30f9fc7e95b9a47630a94921",
+    "tarball": "https://registry.npmjs.org/blob/-/blob-0.0.4.tgz"
+  },
+  "homepage": "https://github.com/rase-/blob",
+  "maintainers": [
+    {
+      "name": "rase-",
+      "email": "tonykovanen@hotmail.com"
+    }
+  ],
+  "name": "blob",
+  "optionalDependencies": {},
+  "readme": "ERROR: No README data found!",
+  "repository": {
+    "type": "git",
+    "url": "git+ssh://git@github.com/rase-/blob.git"
+  },
+  "scripts": {
+    "test": "make test"
+  },
+  "version": "0.0.4"
+}
diff --git a/cep/node_modules/blob/test/index.js b/cep/node_modules/blob/test/index.js
new file mode 100644
index 0000000..df9303f
--- /dev/null
+++ b/cep/node_modules/blob/test/index.js
@@ -0,0 +1,94 @@
+var Blob = require('../');
+var expect = require('expect.js');
+
+describe('blob', function() {
+  if (!Blob) {
+    it('should not have a blob or a blob builder in the global namespace, or blob should not be a constructor function if the module exports false', function() {
+      try {
+        var ab = (new Uint8Array(5)).buffer;
+        global.Blob([ab]);
+        expect().fail('Blob shouldn\'t be constructable');
+      } catch (e) {}
+
+      var BlobBuilder = global.BlobBuilder
+        || global.WebKitBlobBuilder
+        || global.MSBlobBuilder
+        || global.MozBlobBuilder;
+      expect(BlobBuilder).to.be(undefined);
+    });
+  } else {
+    it('should encode a proper sized blob when given a string argument', function() {
+      var b = new Blob(['hi']);
+      expect(b.size).to.be(2);
+    });
+
+    it('should encode a blob with proper size when given two strings as arguments', function() {
+      var b = new Blob(['hi', 'hello']);
+      expect(b.size).to.be(7);
+    });
+
+    it('should encode arraybuffers with right content', function(done) {
+      var ary = new Uint8Array(5);
+      for (var i = 0; i < 5; i++) ary[i] = i;
+      var b = new Blob([ary.buffer]);
+      var fr = new FileReader();
+      fr.onload = function() {
+        var newAry = new Uint8Array(this.result);
+        for (var i = 0; i < 5; i++) expect(newAry[i]).to.be(i);
+        done();
+      };
+      fr.readAsArrayBuffer(b);
+    });
+
+    it('should encode typed arrays with right content', function(done) {
+      var ary = new Uint8Array(5);
+      for (var i = 0; i < 5; i++) ary[i] = i;
+      var b = new Blob([ary]);
+      var fr = new FileReader();
+      fr.onload = function() {
+        var newAry = new Uint8Array(this.result);
+        for (var i = 0; i < 5; i++) expect(newAry[i]).to.be(i);
+        done();
+      };
+      fr.readAsArrayBuffer(b);
+    });
+
+    it('should encode sliced typed arrays with right content', function(done) {
+      var ary = new Uint8Array(5);
+      for (var i = 0; i < 5; i++) ary[i] = i;
+      var b = new Blob([ary.subarray(2)]);
+      var fr = new FileReader();
+      fr.onload = function() {
+        var newAry = new Uint8Array(this.result);
+        for (var i = 0; i < 3; i++) expect(newAry[i]).to.be(i + 2);
+        done();
+      };
+      fr.readAsArrayBuffer(b);
+    });
+
+    it('should encode with blobs', function(done) {
+      var ary = new Uint8Array(5);
+      for (var i = 0; i < 5; i++) ary[i] = i;
+      var b = new Blob([new Blob([ary.buffer])]);
+      var fr = new FileReader();
+      fr.onload = function() {
+        var newAry = new Uint8Array(this.result);
+        for (var i = 0; i < 5; i++) expect(newAry[i]).to.be(i);
+        done();
+      };
+      fr.readAsArrayBuffer(b);
+    });
+
+    it('should enode mixed contents to right size', function() {
+      var ary = new Uint8Array(5);
+      for (var i = 0; i < 5; i++) ary[i] = i;
+      var b = new Blob([ary.buffer, 'hello']);
+      expect(b.size).to.be(10);
+    });
+
+    it('should accept mime type', function() {
+      var b = new Blob(['hi', 'hello'], { type: 'text/html' });
+      expect(b.type).to.be('text/html');
+    });
+  }
+});
diff --git a/cep/node_modules/callsite/.npmignore b/cep/node_modules/callsite/.npmignore
new file mode 100644
index 0000000..f1250e5
--- /dev/null
+++ b/cep/node_modules/callsite/.npmignore
@@ -0,0 +1,4 @@
+support
+test
+examples
+*.sock
diff --git a/cep/node_modules/callsite/History.md b/cep/node_modules/callsite/History.md
new file mode 100644
index 0000000..4994198
--- /dev/null
+++ b/cep/node_modules/callsite/History.md
@@ -0,0 +1,10 @@
+
+1.0.0 / 2013-01-24 
+==================
+
+  * remove lame magical getters
+
+0.0.1 / 2010-01-03
+==================
+
+  * Initial release
diff --git a/cep/node_modules/callsite/Makefile b/cep/node_modules/callsite/Makefile
new file mode 100644
index 0000000..634e372
--- /dev/null
+++ b/cep/node_modules/callsite/Makefile
@@ -0,0 +1,6 @@
+
+test:
+	@./node_modules/.bin/mocha \
+		--require should
+
+.PHONY: test
\ No newline at end of file
diff --git a/cep/node_modules/callsite/Readme.md b/cep/node_modules/callsite/Readme.md
new file mode 100644
index 0000000..0dbd16a
--- /dev/null
+++ b/cep/node_modules/callsite/Readme.md
@@ -0,0 +1,44 @@
+# callstack
+
+  Access to v8's "raw" `CallSite`s.
+
+## Installation
+
+    $ npm install callsite
+
+## Example
+
+```js
+var stack = require('callsite');
+
+foo();
+
+function foo() {
+  bar();
+}
+
+function bar() {
+  baz();
+}
+
+function baz() {
+  console.log();
+  stack().forEach(function(site){
+    console.log('  \033[36m%s\033[90m in %s:%d\033[0m'
+      , site.getFunctionName() || 'anonymous'
+      , site.getFileName()
+      , site.getLineNumber());
+  });
+  console.log();
+}
+```
+
+## Why?
+
+  Because you can do weird, stupid, clever, wacky things such as:
+
+  - [better-assert](https://github.com/visionmedia/better-assert)
+
+## License
+
+  MIT
diff --git a/cep/node_modules/callsite/index.js b/cep/node_modules/callsite/index.js
new file mode 100644
index 0000000..d3ee6f8
--- /dev/null
+++ b/cep/node_modules/callsite/index.js
@@ -0,0 +1,10 @@
+
+module.exports = function(){
+  var orig = Error.prepareStackTrace;
+  Error.prepareStackTrace = function(_, stack){ return stack; };
+  var err = new Error;
+  Error.captureStackTrace(err, arguments.callee);
+  var stack = err.stack;
+  Error.prepareStackTrace = orig;
+  return stack;
+};
diff --git a/cep/node_modules/callsite/package.json b/cep/node_modules/callsite/package.json
new file mode 100644
index 0000000..89fe224
--- /dev/null
+++ b/cep/node_modules/callsite/package.json
@@ -0,0 +1,78 @@
+{
+  "_args": [
+    [
+      {
+        "raw": "callsite@1.0.0",
+        "scope": null,
+        "escapedName": "callsite",
+        "name": "callsite",
+        "rawSpec": "1.0.0",
+        "spec": "1.0.0",
+        "type": "version"
+      },
+      "D:\\repositories\\tk-framework-adobe\\extensions\\http_server\\node_modules\\better-assert"
+    ]
+  ],
+  "_from": "callsite@1.0.0",
+  "_id": "callsite@1.0.0",
+  "_inCache": true,
+  "_installable": true,
+  "_location": "/callsite",
+  "_npmUser": {
+    "name": "tjholowaychuk",
+    "email": "tj@vision-media.ca"
+  },
+  "_npmVersion": "1.2.2",
+  "_phantomChildren": {},
+  "_requested": {
+    "raw": "callsite@1.0.0",
+    "scope": null,
+    "escapedName": "callsite",
+    "name": "callsite",
+    "rawSpec": "1.0.0",
+    "spec": "1.0.0",
+    "type": "version"
+  },
+  "_requiredBy": [
+    "/better-assert"
+  ],
+  "_resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz",
+  "_shasum": "280398e5d664bd74038b6f0905153e6e8af1bc20",
+  "_shrinkwrap": null,
+  "_spec": "callsite@1.0.0",
+  "_where": "D:\\repositories\\tk-framework-adobe\\extensions\\http_server\\node_modules\\better-assert",
+  "author": {
+    "name": "TJ Holowaychuk",
+    "email": "tj@vision-media.ca"
+  },
+  "dependencies": {},
+  "description": "access to v8's CallSites",
+  "devDependencies": {
+    "mocha": "*",
+    "should": "*"
+  },
+  "directories": {},
+  "dist": {
+    "shasum": "280398e5d664bd74038b6f0905153e6e8af1bc20",
+    "tarball": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz"
+  },
+  "engines": {
+    "node": "*"
+  },
+  "keywords": [
+    "stack",
+    "trace",
+    "line"
+  ],
+  "main": "index",
+  "maintainers": [
+    {
+      "name": "tjholowaychuk",
+      "email": "tj@vision-media.ca"
+    }
+  ],
+  "name": "callsite",
+  "optionalDependencies": {},
+  "readme": "ERROR: No README data found!",
+  "version": "1.0.0"
+}
diff --git a/cep/node_modules/component-bind/.npmignore b/cep/node_modules/component-bind/.npmignore
new file mode 100644
index 0000000..f1250e5
--- /dev/null
+++ b/cep/node_modules/component-bind/.npmignore
@@ -0,0 +1,4 @@
+support
+test
+examples
+*.sock
diff --git a/cep/node_modules/component-bind/History.md b/cep/node_modules/component-bind/History.md
new file mode 100644
index 0000000..2795fdb
--- /dev/null
+++ b/cep/node_modules/component-bind/History.md
@@ -0,0 +1,13 @@
+
+1.0.0 / 2014-05-27
+==================
+
+  * index: use slice ref (#7, @viatropos)
+  * package: rename package to "component-bind"
+  * package: add "repository" field (#6, @repoify)
+  * package: add "component" section
+
+0.0.1 / 2010-01-03
+==================
+
+  * Initial release
diff --git a/cep/node_modules/component-bind/Makefile b/cep/node_modules/component-bind/Makefile
new file mode 100644
index 0000000..4e9c8d3
--- /dev/null
+++ b/cep/node_modules/component-bind/Makefile
@@ -0,0 +1,7 @@
+
+test:
+	@./node_modules/.bin/mocha \
+		--require should \
+		--reporter spec
+
+.PHONY: test
\ No newline at end of file
diff --git a/cep/node_modules/component-bind/Readme.md b/cep/node_modules/component-bind/Readme.md
new file mode 100644
index 0000000..6a8febc
--- /dev/null
+++ b/cep/node_modules/component-bind/Readme.md
@@ -0,0 +1,64 @@
+# bind
+
+  Function binding utility.
+
+## Installation
+
+```
+$ component install component/bind
+```
+
+## API
+
+   - [bind(obj, fn)](#bindobj-fn)
+   - [bind(obj, fn, ...)](#bindobj-fn-)
+   - [bind(obj, name)](#bindobj-name)
+
+ 
+
+### bind(obj, fn)
+should bind the function to the given object.
+
+```js
+var tobi = { name: 'tobi' };
+
+function name() {
+  return this.name;
+}
+
+var fn = bind(tobi, name);
+fn().should.equal('tobi');
+```
+
+
+### bind(obj, fn, ...)
+should curry the remaining arguments.
+
+```js
+function add(a, b) {
+  return a + b;
+}
+
+bind(null, add)(1, 2).should.equal(3);
+bind(null, add, 1)(2).should.equal(3);
+bind(null, add, 1, 2)().should.equal(3);
+```
+
+
+### bind(obj, name)
+should bind the method of the given name.
+
+```js
+var tobi = { name: 'tobi' };
+
+tobi.getName = function() {
+  return this.name;
+};
+
+var fn = bind(tobi, 'getName');
+fn().should.equal('tobi');
+```
+
+## License 
+
+  MIT
\ No newline at end of file
diff --git a/cep/node_modules/component-bind/component.json b/cep/node_modules/component-bind/component.json
new file mode 100644
index 0000000..4e1e93f
--- /dev/null
+++ b/cep/node_modules/component-bind/component.json
@@ -0,0 +1,13 @@
+{
+  "name": "bind",
+  "version": "1.0.0",
+  "description": "function binding utility",
+  "keywords": [
+    "bind",
+    "utility"
+  ],
+  "dependencies": {},
+  "scripts": [
+    "index.js"
+  ]
+}
diff --git a/cep/node_modules/component-bind/index.js b/cep/node_modules/component-bind/index.js
new file mode 100644
index 0000000..4eeb2c0
--- /dev/null
+++ b/cep/node_modules/component-bind/index.js
@@ -0,0 +1,23 @@
+/**
+ * Slice reference.
+ */
+
+var slice = [].slice;
+
+/**
+ * Bind `obj` to `fn`.
+ *
+ * @param {Object} obj
+ * @param {Function|String} fn or string
+ * @return {Function}
+ * @api public
+ */
+
+module.exports = function(obj, fn){
+  if ('string' == typeof fn) fn = obj[fn];
+  if ('function' != typeof fn) throw new Error('bind() requires a function');
+  var args = slice.call(arguments, 2);
+  return function(){
+    return fn.apply(obj, args.concat(slice.call(arguments)));
+  }
+};
diff --git a/cep/node_modules/component-bind/package.json b/cep/node_modules/component-bind/package.json
new file mode 100644
index 0000000..bffed85
--- /dev/null
+++ b/cep/node_modules/component-bind/package.json
@@ -0,0 +1,82 @@
+{
+  "_args": [
+    [
+      {
+        "raw": "component-bind@1.0.0",
+        "scope": null,
+        "escapedName": "component-bind",
+        "name": "component-bind",
+        "rawSpec": "1.0.0",
+        "spec": "1.0.0",
+        "type": "version"
+      },
+      "D:\\repositories\\tk-framework-adobe\\extensions\\http_server\\node_modules\\socket.io-client"
+    ]
+  ],
+  "_from": "component-bind@1.0.0",
+  "_id": "component-bind@1.0.0",
+  "_inCache": true,
+  "_installable": true,
+  "_location": "/component-bind",
+  "_npmUser": {
+    "name": "tootallnate",
+    "email": "nathan@tootallnate.net"
+  },
+  "_npmVersion": "1.4.9",
+  "_phantomChildren": {},
+  "_requested": {
+    "raw": "component-bind@1.0.0",
+    "scope": null,
+    "escapedName": "component-bind",
+    "name": "component-bind",
+    "rawSpec": "1.0.0",
+    "spec": "1.0.0",
+    "type": "version"
+  },
+  "_requiredBy": [
+    "/socket.io-client"
+  ],
+  "_resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz",
+  "_shasum": "00c608ab7dcd93897c0009651b1d3a8e1e73bbd1",
+  "_shrinkwrap": null,
+  "_spec": "component-bind@1.0.0",
+  "_where": "D:\\repositories\\tk-framework-adobe\\extensions\\http_server\\node_modules\\socket.io-client",
+  "bugs": {
+    "url": "https://github.com/component/bind/issues"
+  },
+  "component": {
+    "scripts": {
+      "bind/index.js": "index.js"
+    }
+  },
+  "dependencies": {},
+  "description": "function binding utility",
+  "devDependencies": {
+    "mocha": "*",
+    "should": "*"
+  },
+  "directories": {},
+  "dist": {
+    "shasum": "00c608ab7dcd93897c0009651b1d3a8e1e73bbd1",
+    "tarball": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz"
+  },
+  "homepage": "https://github.com/component/bind",
+  "keywords": [
+    "bind",
+    "utility"
+  ],
+  "maintainers": [
+    {
+      "name": "tootallnate",
+      "email": "nathan@tootallnate.net"
+    }
+  ],
+  "name": "component-bind",
+  "optionalDependencies": {},
+  "readme": "ERROR: No README data found!",
+  "repository": {
+    "type": "git",
+    "url": "git+https://github.com/component/bind.git"
+  },
+  "version": "1.0.0"
+}
diff --git a/cep/node_modules/component-emitter/.npmignore b/cep/node_modules/component-emitter/.npmignore
new file mode 100644
index 0000000..f05b1f2
--- /dev/null
+++ b/cep/node_modules/component-emitter/.npmignore
@@ -0,0 +1,2 @@
+node_modules
+test
diff --git a/cep/node_modules/component-emitter/.travis.yml b/cep/node_modules/component-emitter/.travis.yml
new file mode 100644
index 0000000..8750e3b
--- /dev/null
+++ b/cep/node_modules/component-emitter/.travis.yml
@@ -0,0 +1,4 @@
+node_js:
+- "0.8"
+- "0.10"
+language: node_js
\ No newline at end of file
diff --git a/cep/node_modules/component-emitter/History.md b/cep/node_modules/component-emitter/History.md
new file mode 100644
index 0000000..b898ca6
--- /dev/null
+++ b/cep/node_modules/component-emitter/History.md
@@ -0,0 +1,52 @@
+
+1.1.2 / 2014-02-10
+==================
+
+  * package: rename to "component-emitter"
+  * package: update "main" and "component" fields
+  * Add license to Readme (same format as the other components)
+  * created .npmignore
+  * travis stuff
+
+1.1.1 / 2013-12-01
+==================
+
+  * fix .once adding .on to the listener
+  * docs: Emitter#off()
+  * component: add `.repo` prop
+
+1.1.0 / 2013-10-20
+==================
+
+ * add `.addEventListener()` and `.removeEventListener()` aliases
+
+1.0.1 / 2013-06-27
+==================
+
+ * add support for legacy ie
+
+1.0.0 / 2013-02-26
+==================
+
+  * add `.off()` support for removing all listeners
+
+0.0.6 / 2012-10-08
+==================
+
+  * add `this._callbacks` initialization to prevent funky gotcha
+
+0.0.5 / 2012-09-07
+==================
+
+  * fix `Emitter.call(this)` usage
+
+0.0.3 / 2012-07-11
+==================
+
+  * add `.listeners()`
+  * rename `.has()` to `.hasListeners()`
+
+0.0.2 / 2012-06-28
+==================
+
+  * fix `.off()` with `.once()`-registered callbacks
diff --git a/cep/node_modules/component-emitter/Makefile b/cep/node_modules/component-emitter/Makefile
new file mode 100644
index 0000000..4e9c8d3
--- /dev/null
+++ b/cep/node_modules/component-emitter/Makefile
@@ -0,0 +1,7 @@
+
+test:
+	@./node_modules/.bin/mocha \
+		--require should \
+		--reporter spec
+
+.PHONY: test
\ No newline at end of file
diff --git a/cep/node_modules/component-emitter/Readme.md b/cep/node_modules/component-emitter/Readme.md
new file mode 100644
index 0000000..0f3f9b9
--- /dev/null
+++ b/cep/node_modules/component-emitter/Readme.md
@@ -0,0 +1,74 @@
+# Emitter [![Build Status](https://travis-ci.org/component/emitter.png)](https://travis-ci.org/component/emitter)
+
+  Event emitter component.
+
+## Installation
+
+```
+$ component install component/emitter
+```
+
+## API
+
+### Emitter(obj)
+
+  The `Emitter` may also be used as a mixin. For example
+  a "plain" object may become an emitter, or you may
+  extend an existing prototype.
+
+  As an `Emitter` instance:
+
+```js
+var Emitter = require('emitter');
+var emitter = new Emitter;
+emitter.emit('something');
+```
+
+  As a mixin:
+
+```js
+var Emitter = require('emitter');
+var user = { name: 'tobi' };
+Emitter(user);
+
+user.emit('im a user');
+```
+
+  As a prototype mixin:
+
+```js
+var Emitter = require('emitter');
+Emitter(User.prototype);
+```
+
+### Emitter#on(event, fn)
+
+  Register an `event` handler `fn`.
+
+### Emitter#once(event, fn)
+
+  Register a single-shot `event` handler `fn`,
+  removed immediately after it is invoked the
+  first time.
+
+### Emitter#off(event, fn)
+
+  * Pass `event` and `fn` to remove a listener.
+  * Pass `event` to remove all listeners on that event.
+  * Pass nothing to remove all listeners on all events.
+
+### Emitter#emit(event, ...)
+
+  Emit an `event` with variable option args.
+
+### Emitter#listeners(event)
+
+  Return an array of callbacks, or an empty array.
+
+### Emitter#hasListeners(event)
+
+  Check if this emitter has `event` handlers.
+
+## License
+
+MIT
diff --git a/cep/node_modules/component-emitter/bower.json b/cep/node_modules/component-emitter/bower.json
new file mode 100644
index 0000000..c618d41
--- /dev/null
+++ b/cep/node_modules/component-emitter/bower.json
@@ -0,0 +1,21 @@
+{
+  "name": "emitter",
+  "description": "Event emitter",
+  "keywords": [
+    "emitter",
+    "events"
+  ],
+  "version": "1.1.2",
+  "license": "MIT",
+  "main": "index.js",
+  "homepage": "https://github.com/component/emitter",
+  "ignore": [
+    "**/.*",
+    "node_modules",
+    "bower_components",
+    "test",
+    "Makefile",
+    "package.json",
+    "component.json"
+  ]
+}
diff --git a/cep/node_modules/component-emitter/component.json b/cep/node_modules/component-emitter/component.json
new file mode 100644
index 0000000..68ba0b1
--- /dev/null
+++ b/cep/node_modules/component-emitter/component.json
@@ -0,0 +1,14 @@
+{
+  "name": "emitter",
+  "repo": "component/emitter",
+  "description": "Event emitter",
+  "keywords": [
+    "emitter",
+    "events"
+  ],
+  "version": "1.1.2",
+  "scripts": [
+    "index.js"
+  ],
+  "license": "MIT"
+}
diff --git a/cep/node_modules/component-emitter/index.js b/cep/node_modules/component-emitter/index.js
new file mode 100644
index 0000000..ad71163
--- /dev/null
+++ b/cep/node_modules/component-emitter/index.js
@@ -0,0 +1,164 @@
+
+/**
+ * Expose `Emitter`.
+ */
+
+module.exports = Emitter;
+
+/**
+ * Initialize a new `Emitter`.
+ *
+ * @api public
+ */
+
+function Emitter(obj) {
+  if (obj) return mixin(obj);
+};
+
+/**
+ * Mixin the emitter properties.
+ *
+ * @param {Object} obj
+ * @return {Object}
+ * @api private
+ */
+
+function mixin(obj) {
+  for (var key in Emitter.prototype) {
+    obj[key] = Emitter.prototype[key];
+  }
+  return obj;
+}
+
+/**
+ * Listen on the given `event` with `fn`.
+ *
+ * @param {String} event
+ * @param {Function} fn
+ * @return {Emitter}
+ * @api public
+ */
+
+Emitter.prototype.on =
+Emitter.prototype.addEventListener = function(event, fn){
+  this._callbacks = this._callbacks || {};
+  (this._callbacks[event] = this._callbacks[event] || [])
+    .push(fn);
+  return this;
+};
+
+/**
+ * Adds an `event` listener that will be invoked a single
+ * time then automatically removed.
+ *
+ * @param {String} event
+ * @param {Function} fn
+ * @return {Emitter}
+ * @api public
+ */
+
+Emitter.prototype.once = function(event, fn){
+  var self = this;
+  this._callbacks = this._callbacks || {};
+
+  function on() {
+    self.off(event, on);
+    fn.apply(this, arguments);
+  }
+
+  on.fn = fn;
+  this.on(event, on);
+  return this;
+};
+
+/**
+ * Remove the given callback for `event` or all
+ * registered callbacks.
+ *
+ * @param {String} event
+ * @param {Function} fn
+ * @return {Emitter}
+ * @api public
+ */
+
+Emitter.prototype.off =
+Emitter.prototype.removeListener =
+Emitter.prototype.removeAllListeners =
+Emitter.prototype.removeEventListener = function(event, fn){
+  this._callbacks = this._callbacks || {};
+
+  // all
+  if (0 == arguments.length) {
+    this._callbacks = {};
+    return this;
+  }
+
+  // specific event
+  var callbacks = this._callbacks[event];
+  if (!callbacks) return this;
+
+  // remove all handlers
+  if (1 == arguments.length) {
+    delete this._callbacks[event];
+    return this;
+  }
+
+  // remove specific handler
+  var cb;
+  for (var i = 0; i < callbacks.length; i++) {
+    cb = callbacks[i];
+    if (cb === fn || cb.fn === fn) {
+      callbacks.splice(i, 1);
+      break;
+    }
+  }
+  return this;
+};
+
+/**
+ * Emit `event` with the given args.
+ *
+ * @param {String} event
+ * @param {Mixed} ...
+ * @return {Emitter}
+ */
+
+Emitter.prototype.emit = function(event){
+  this._callbacks = this._callbacks || {};
+  var args = [].slice.call(arguments, 1)
+    , callbacks = this._callbacks[event];
+
+  if (callbacks) {
+    callbacks = callbacks.slice(0);
+    for (var i = 0, len = callbacks.length; i < len; ++i) {
+      callbacks[i].apply(this, args);
+    }
+  }
+
+  return this;
+};
+
+/**
+ * Return array of callbacks for `event`.
+ *
+ * @param {String} event
+ * @return {Array}
+ * @api public
+ */
+
+Emitter.prototype.listeners = function(event){
+  this._callbacks = this._callbacks || {};
+  return this._callbacks[event] || [];
+};
+
+/**
+ * Check if this emitter has `event` handlers.
+ *
+ * @param {String} event
+ * @return {Boolean}
+ * @api public
+ */
+
+Emitter.prototype.hasListeners = function(event){
+  return !! this.listeners(event).length;
+};
diff --git a/cep/node_modules/component-emitter/package.json b/cep/node_modules/component-emitter/package.json
new file mode 100644
index 0000000..f735419
--- /dev/null
+++ b/cep/node_modules/component-emitter/package.json
@@ -0,0 +1,84 @@
+{
+  "_args": [
+    [
+      {
+        "raw": "component-emitter@1.1.2",
+        "scope": null,
+        "escapedName": "component-emitter",
+        "name": "component-emitter",
+        "rawSpec": "1.1.2",
+        "spec": "1.1.2",
+        "type": "version"
+      },
+      "D:\\repositories\\tk-framework-adobe\\extensions\\http_server\\node_modules\\socket.io-adapter\\node_modules\\socket.io-parser"
+    ]
+  ],
+  "_from": "component-emitter@1.1.2",
+  "_id": "component-emitter@1.1.2",
+  "_inCache": true,
+  "_installable": true,
+  "_location": "/component-emitter",
+  "_npmUser": {
+    "name": "tootallnate",
+    "email": "nathan@tootallnate.net"
+  },
+  "_npmVersion": "1.3.24",
+  "_phantomChildren": {},
+  "_requested": {
+    "raw": "component-emitter@1.1.2",
+    "scope": null,
+    "escapedName": "component-emitter",
+    "name": "component-emitter",
+    "rawSpec": "1.1.2",
+    "spec": "1.1.2",
+    "type": "version"
+  },
+  "_requiredBy": [
+    "/engine.io-client",
+    "/socket.io-adapter/socket.io-parser",
+    "/socket.io-parser"
+  ],
+  "_resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.1.2.tgz",
+  "_shasum": "296594f2753daa63996d2af08d15a95116c9aec3",
+  "_shrinkwrap": null,
+  "_spec": "component-emitter@1.1.2",
+  "_where": "D:\\repositories\\tk-framework-adobe\\extensions\\http_server\\node_modules\\socket.io-adapter\\node_modules\\socket.io-parser",
+  "bugs": {
+    "url": "https://github.com/component/emitter/issues"
+  },
+  "component": {
+    "scripts": {
+      "emitter/index.js": "index.js"
+    }
+  },
+  "dependencies": {},
+  "description": "Event emitter",
+  "devDependencies": {
+    "mocha": "*",
+    "should": "*"
+  },
+  "directories": {},
+  "dist": {
+    "shasum": "296594f2753daa63996d2af08d15a95116c9aec3",
+    "tarball": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.1.2.tgz"
+  },
+  "homepage": "https://github.com/component/emitter",
+  "main": "index.js",
+  "maintainers": [
+    {
+      "name": "tootallnate",
+      "email": "nathan@tootallnate.net"
+    }
+  ],
+  "name": "component-emitter",
+  "optionalDependencies": {},
+  "readme": "ERROR: No README data found!",
+  "repository": {
+    "type": "git",
+    "url": "git+https://github.com/component/emitter.git"
+  },
+  "scripts": {
+    "test": "make test"
+  },
+  "version": "1.1.2"
+}
diff --git a/cep/node_modules/component-inherit/.npmignore b/cep/node_modules/component-inherit/.npmignore
new file mode 100644
index 0000000..665aa21
--- /dev/null
+++ b/cep/node_modules/component-inherit/.npmignore
@@ -0,0 +1,3 @@
+components
+build
+node_modules
diff --git a/cep/node_modules/component-inherit/History.md b/cep/node_modules/component-inherit/History.md
new file mode 100644
index 0000000..22d87e1
--- /dev/null
+++ b/cep/node_modules/component-inherit/History.md
@@ -0,0 +1,5 @@
+
+0.0.2 / 2012-09-03 
+==================
+
+  * fix typo in package.json
diff --git a/cep/node_modules/component-inherit/Makefile b/cep/node_modules/component-inherit/Makefile
new file mode 100644
index 0000000..ebbc52a
--- /dev/null
+++ b/cep/node_modules/component-inherit/Makefile
@@ -0,0 +1,16 @@
+
+build: components index.js
+	@component build
+
+components:
+	@Component install
+
+clean:
+	rm -fr build components template.js
+
+test:
+	@node_modules/.bin/mocha \
+		--require should \
+		--reporter spec
+
+.PHONY: clean test
diff --git a/cep/node_modules/component-inherit/Readme.md b/cep/node_modules/component-inherit/Readme.md
new file mode 100644
index 0000000..f03ab27
--- /dev/null
+++ b/cep/node_modules/component-inherit/Readme.md
@@ -0,0 +1,24 @@
+# inherit
+
+  Prototype inheritance utility.
+
+## Installation
+
+```
+$ component install component/inherit
+```
+
+## Example
+
+```js
+var inherit = require('inherit');
+
+function Human() {}
+function Woman() {}
+
+inherit(Woman, Human);
+```
+
+## License
+
+  MIT
diff --git a/cep/node_modules/component-inherit/component.json b/cep/node_modules/component-inherit/component.json
new file mode 100644
index 0000000..ae57747
--- /dev/null
+++ b/cep/node_modules/component-inherit/component.json
@@ -0,0 +1,10 @@
+{
+  "name": "inherit",
+  "description": "Prototype inheritance utility",
+  "version": "0.0.3",
+  "keywords": ["inherit", "utility"],
+  "dependencies": {},
+  "scripts": [
+    "index.js"
+  ]
+}
diff --git a/cep/node_modules/component-inherit/index.js b/cep/node_modules/component-inherit/index.js
new file mode 100644
index 0000000..aaebc03
--- /dev/null
+++ b/cep/node_modules/component-inherit/index.js
@@ -0,0 +1,7 @@
+
+module.exports = function(a, b){
+  var fn = function(){};
+  fn.prototype = b.prototype;
+  a.prototype = new fn;
+  a.prototype.constructor = a;
+};
\ No newline at end of file
diff --git a/cep/node_modules/component-inherit/package.json b/cep/node_modules/component-inherit/package.json
new file mode 100644
index 0000000..6f5357e
--- /dev/null
+++ b/cep/node_modules/component-inherit/package.json
@@ -0,0 +1,79 @@
+{
+  "_args": [
+    [
+      {
+        "raw": "component-inherit@0.0.3",
+        "scope": null,
+        "escapedName": "component-inherit",
+        "name": "component-inherit",
+        "rawSpec": "0.0.3",
+        "spec": "0.0.3",
+        "type": "version"
+      },
+      "D:\\repositories\\tk-framework-adobe\\extensions\\http_server\\node_modules\\engine.io-client"
+    ]
+  ],
+  "_from": "component-inherit@0.0.3",
+  "_id": "component-inherit@0.0.3",
+  "_inCache": true,
+  "_installable": true,
+  "_location": "/component-inherit",
+  "_npmUser": {
+    "name": "coreh",
+    "email": "thecoreh@gmail.com"
+  },
+  "_npmVersion": "1.3.24",
+  "_phantomChildren": {},
+  "_requested": {
+    "raw": "component-inherit@0.0.3",
+    "scope": null,
+    "escapedName": "component-inherit",
+    "name": "component-inherit",
+    "rawSpec": "0.0.3",
+    "spec": "0.0.3",
+    "type": "version"
+  },
+  "_requiredBy": [
+    "/engine.io-client"
+  ],
+  "_resolved": "https://registry.npmjs.org/component-inherit/-/component-inherit-0.0.3.tgz",
+  "_shasum": "645fc4adf58b72b649d5cae65135619db26ff143",
+  "_shrinkwrap": null,
+  "_spec": "component-inherit@0.0.3",
+  "_where": "D:\\repositories\\tk-framework-adobe\\extensions\\http_server\\node_modules\\engine.io-client",
+  "bugs": {
+    "url": "https://github.com/component/inherit/issues"
+  },
+  "component": {
+    "scripts": {
+      "inherit/index.js": "index.js"
+    }
+  },
+  "dependencies": {},
+  "description": "Prototype inheritance utility",
+  "devDependencies": {},
+  "directories": {},
+  "dist": {
+    "shasum": "645fc4adf58b72b649d5cae65135619db26ff143",
+    "tarball": "https://registry.npmjs.org/component-inherit/-/component-inherit-0.0.3.tgz"
+  },
+  "homepage": "https://github.com/component/inherit",
+  "keywords": [
+    "inherit",
+    "utility"
+  ],
+  "maintainers": [
+    {
+      "name": "coreh",
+      "email": "thecoreh@gmail.com"
+    }
+  ],
+  "name": "component-inherit",
+  "optionalDependencies": {},
+  "readme": "ERROR: No README data found!",
+  "repository": {
+    "type": "git",
+    "url": "git+https://github.com/component/inherit.git"
+  },
+  "version": "0.0.3"
+}
diff --git a/cep/node_modules/component-inherit/test/inherit.js b/cep/node_modules/component-inherit/test/inherit.js
new file mode 100644
index 0000000..14852f2
--- /dev/null
+++ b/cep/node_modules/component-inherit/test/inherit.js
@@ -0,0 +1,21 @@
+
+/**
+ * Module dependencies.
+ */
+
+var inherit = require('..');
+
+describe('inherit(a, b)', function(){
+  it('should inherit b\'s prototype', function(){
+    function Loki(){}
+    function Animal(){}
+
+    Animal.prototype.species = 'unknown';
+
+    inherit(Loki, Animal);
+
+    var loki = new Loki;
+    loki.species.should.equal('unknown');
+    loki.constructor.should.equal(Loki);
+  })
+})
\ No newline at end of file
diff --git a/cep/node_modules/debug/.jshintrc b/cep/node_modules/debug/.jshintrc
new file mode 100644
index 0000000..299877f
--- /dev/null
+++ b/cep/node_modules/debug/.jshintrc
@@ -0,0 +1,3 @@
+{
+  "laxbreak": true
+}
diff --git a/cep/node_modules/debug/.npmignore b/cep/node_modules/debug/.npmignore
new file mode 100644
index 0000000..7e6163d
--- /dev/null
+++ b/cep/node_modules/debug/.npmignore
@@ -0,0 +1,6 @@
+support
+test
+examples
+example
+*.sock
+dist
diff --git a/cep/node_modules/debug/History.md b/cep/node_modules/debug/History.md
new file mode 100644
index 0000000..854c971
--- /dev/null
+++ b/cep/node_modules/debug/History.md
@@ -0,0 +1,195 @@
+
+2.2.0 / 2015-05-09
+==================
+
+  * package: update "ms" to v0.7.1 (#202, @dougwilson)
+  * README: add logging to file example (#193, @DanielOchoa)
+  * README: fixed a typo (#191, @amir-s)
+  * browser: expose `storage` (#190, @stephenmathieson)
+  * Makefile: add a `distclean` target (#189, @stephenmathieson)
+
+2.1.3 / 2015-03-13
+==================
+
+  * Updated stdout/stderr example (#186)
+  * Updated example/stdout.js to match debug current behaviour
+  * Renamed example/stderr.js to stdout.js
+  * Update Readme.md (#184)
+  * replace high intensity foreground color for bold (#182, #183)
+
+2.1.2 / 2015-03-01
+==================
+
+  * dist: recompile
+  * update "ms" to v0.7.0
+  * package: update "browserify" to v9.0.3
+  * component: fix "ms.js" repo location
+  * changed bower package name
+  * updated documentation about using debug in a browser
+  * fix: security error on safari (#167, #168, @yields)
+
+2.1.1 / 2014-12-29
+==================
+
+  * browser: use `typeof` to check for `console` existence
+  * browser: check for `console.log` truthiness (fix IE 8/9)
+  * browser: add support for Chrome apps
+  * Readme: added Windows usage remarks
+  * Add `bower.json` to properly support bower install
+
+2.1.0 / 2014-10-15
+==================
+
+  * node: implement `DEBUG_FD` env variable support
+  * package: update "browserify" to v6.1.0
+  * package: add "license" field to package.json (#135, @panuhorsmalahti)
+
+2.0.0 / 2014-09-01
+==================
+
+  * package: update "browserify" to v5.11.0
+  * node: use stderr rather than stdout for logging (#29, @stephenmathieson)
+
+1.0.4 / 2014-07-15
+==================
+
+  * dist: recompile
+  * example: remove `console.info()` log usage
+  * example: add "Content-Type" UTF-8 header to browser example
+  * browser: place %c marker after the space character
+  * browser: reset the "content" color via `color: inherit`
+  * browser: add colors support for Firefox >= v31
+  * debug: prefer an instance `log()` function over the global one (#119)
+  * Readme: update documentation about styled console logs for FF v31 (#116, @wryk)
+
+1.0.3 / 2014-07-09
+==================
+
+  * Add support for multiple wildcards in namespaces (#122, @seegno)
+  * browser: fix lint
+
+1.0.2 / 2014-06-10
+==================
+
+  * browser: update color palette (#113, @gscottolson)
+  * common: make console logging function configurable (#108, @timoxley)
+  * node: fix %o colors on old node <= 0.8.x
+  * Makefile: find node path using shell/which (#109, @timoxley)
+
+1.0.1 / 2014-06-06
+==================
+
+  * browser: use `removeItem()` to clear localStorage
+  * browser, node: don't set DEBUG if namespaces is undefined (#107, @leedm777)
+  * package: add "contributors" section
+  * node: fix comment typo
+  * README: list authors
+
+1.0.0 / 2014-06-04
+==================
+
+  * make ms diff be global, not be scope
+  * debug: ignore empty strings in enable()
+  * node: make DEBUG_COLORS able to disable coloring
+  * *: export the `colors` array
+  * npmignore: don't publish the `dist` dir
+  * Makefile: refactor to use browserify
+  * package: add "browserify" as a dev dependency
+  * Readme: add Web Inspector Colors section
+  * node: reset terminal color for the debug content
+  * node: map "%o" to `util.inspect()`
+  * browser: map "%j" to `JSON.stringify()`
+  * debug: add custom "formatters"
+  * debug: use "ms" module for humanizing the diff
+  * Readme: add "bash" syntax highlighting
+  * browser: add Firebug color support
+  * browser: add colors for WebKit browsers
+  * node: apply log to `console`
+  * rewrite: abstract common logic for Node & browsers
+  * add .jshintrc file
+
+0.8.1 / 2014-04-14
+==================
+
+  * package: re-add the "component" section
+
+0.8.0 / 2014-03-30
+==================
+
+  * add `enable()` method for nodejs. Closes #27
+  * change from stderr to stdout
+  * remove unnecessary index.js file
+
+0.7.4 / 2013-11-13
+==================
+
+  * remove "browserify" key from package.json (fixes something in browserify)
+
+0.7.3 / 2013-10-30
+==================
+
+  * fix: catch localStorage security error when cookies are blocked (Chrome)
+  * add debug(err) support. Closes #46
+  * add .browser prop to package.json. Closes #42
+
+0.7.2 / 2013-02-06
+==================
+
+  * fix package.json
+  * fix: Mobile Safari (private mode) is broken with debug
+  * fix: Use unicode to send escape character to shell instead of octal to work with strict mode javascript
+
+0.7.1 / 2013-02-05
+==================
+
+  * add repository URL to package.json
+  * add DEBUG_COLORED to force colored output
+  * add browserify support
+  * fix component. Closes #24
+
+0.7.0 / 2012-05-04
+==================
+
+  * Added .component to package.json
+  * Added debug.component.js build
+
+0.6.0 / 2012-03-16
+==================
+
+  * Added support for "-" prefix in DEBUG [Vinay Pulim]
+  * Added `.enabled` flag to the node version [TooTallNate]
+
+0.5.0 / 2012-02-02
+==================
+
+  * Added: humanize diffs. Closes #8
+  * Added `debug.disable()` to the CS variant
+  * Removed padding. Closes #10
+  * Fixed: persist client-side variant again. Closes #9
+
+0.4.0 / 2012-02-01
+==================
+
+  * Added browser variant support for older browsers [TooTallNate]
+  * Added `debug.enable('project:*')` to browser variant [TooTallNate]
+  * Added padding to diff (moved it to the right)
+
+0.3.0 / 2012-01-26
+==================
+
+  * Added millisecond diff when isatty, otherwise UTC string
+
+0.2.0 / 2012-01-22
+==================
+
+  * Added wildcard support
+
+0.1.0 / 2011-12-02
+==================
+
+  * Added: remove colors unless stderr isatty [TooTallNate]
+
+0.0.1 / 2010-01-03
+==================
+
+  * Initial release
diff --git a/cep/node_modules/debug/Makefile b/cep/node_modules/debug/Makefile
new file mode 100644
index 0000000..5cf4a59
--- /dev/null
+++ b/cep/node_modules/debug/Makefile
@@ -0,0 +1,36 @@
+
+# get Makefile directory name: http://stackoverflow.com/a/5982798/376773
+THIS_MAKEFILE_PATH:=$(word $(words $(MAKEFILE_LIST)),$(MAKEFILE_LIST))
+THIS_DIR:=$(shell cd $(dir $(THIS_MAKEFILE_PATH));pwd)
+
+# BIN directory
+BIN := $(THIS_DIR)/node_modules/.bin
+
+# applications
+NODE ?= $(shell which node)
+NPM ?= $(NODE) $(shell which npm)
+BROWSERIFY ?= $(NODE) $(BIN)/browserify
+
+all: dist/debug.js
+
+install: node_modules
+
+clean:
+	@rm -rf dist
+
+dist:
+	@mkdir -p $@
+
+dist/debug.js: node_modules browser.js debug.js dist
+	@$(BROWSERIFY) \
+		--standalone debug \
+		. > $@
+
+distclean: clean
+	@rm -rf node_modules
+
+node_modules: package.json
+	@NODE_ENV= $(NPM) install
+	@touch node_modules
+
+.PHONY: all install clean distclean
diff --git a/cep/node_modules/debug/Readme.md b/cep/node_modules/debug/Readme.md
new file mode 100644
index 0000000..b4f45e3
--- /dev/null
+++ b/cep/node_modules/debug/Readme.md
@@ -0,0 +1,188 @@
+# debug
+
+  tiny node.js debugging utility modelled after node core's debugging technique.
+
+## Installation
+
+```bash
+$ npm install debug
+```
+
+## Usage
+
+ With `debug` you simply invoke the exported function to generate your debug function, passing it a name which will determine if a noop function is returned, or a decorated `console.error`, so all of the `console` format string goodies you're used to work fine. A unique color is selected per-function for visibility.
+
+Example _app.js_:
+
+```js
+var debug = require('debug')('http')
+  , http = require('http')
+  , name = 'My App';
+
+// fake app
+
+debug('booting %s', name);
+
+http.createServer(function(req, res){
+  debug(req.method + ' ' + req.url);
+  res.end('hello\n');
+}).listen(3000, function(){
+  debug('listening');
+});
+
+// fake worker of some kind
+
+require('./worker');
+```
+
+Example _worker.js_:
+
+```js
+var debug = require('debug')('worker');
+
+setInterval(function(){
+  debug('doing some work');
+}, 1000);
+```
+
+ The __DEBUG__ environment variable is then used to enable these based on space or comma-delimited names. Here are some examples:
+
+  ![debug http and worker](http://f.cl.ly/items/18471z1H402O24072r1J/Screenshot.png)
+
+  ![debug worker](http://f.cl.ly/items/1X413v1a3M0d3C2c1E0i/Screenshot.png)
+
+#### Windows note
+
+ On Windows the environment variable is set using the `set` command.
+
+ ```cmd
+ set DEBUG=*,-not_this
+ ```
+
+Then, run the program to be debugged as usual.
+
+## Millisecond diff
+
+  When actively developing an application it can be useful to see when the time spent between one `debug()` call and the next. Suppose for example you invoke `debug()` before requesting a resource, and after as well, the "+NNNms" will show you how much time was spent between calls.
+
+  ![](http://f.cl.ly/items/2i3h1d3t121M2Z1A3Q0N/Screenshot.png)
+
+  When stdout is not a TTY, `Date#toUTCString()` is used, making it more useful for logging the debug information as shown below:
+
+  ![](http://f.cl.ly/items/112H3i0e0o0P0a2Q2r11/Screenshot.png)
+
+## Conventions
+
+ If you're using this in one or more of your libraries, you _should_ use the name of your library so that developers may toggle debugging as desired without guessing names. If you have more than one debuggers you _should_ prefix them with your library name and use ":" to separate features. For example "bodyParser" from Connect would then be "connect:bodyParser".
+
+## Wildcards
+
+  The `*` character may be used as a wildcard. Suppose for example your library has debuggers named "connect:bodyParser", "connect:compress", "connect:session", instead of listing all three with `DEBUG=connect:bodyParser,connect:compress,connect:session`, you may simply do `DEBUG=connect:*`, or to run everything using this module simply use `DEBUG=*`.
+
+  You can also exclude specific debuggers by prefixing them with a "-" character.  For example, `DEBUG=*,-connect:*` would include all debuggers except those starting with "connect:".
+
+## Browser support
+
+  Debug works in the browser as well, currently persisted by `localStorage`. Consider the situation shown below where you have `worker:a` and `worker:b`, and wish to debug both. Somewhere in the code on your page, include:
+
+```js
+window.myDebug = require("debug");
+```
+
+  ("debug" is a global object in the browser so we give this object a different name.) When your page is open in the browser, type the following in the console:
+
+```js
+myDebug.enable("worker:*")
+```
+
+  Refresh the page. Debug output will continue to be sent to the console until it is disabled by typing `myDebug.disable()` in the console.
+
+```js
+a = debug('worker:a');
+b = debug('worker:b');
+
+setInterval(function(){
+  a('doing some work');
+}, 1000);
+
+setInterval(function(){
+  b('doing some work');
+}, 1200);
+```
+
+#### Web Inspector Colors
+
+  Colors are also enabled on "Web Inspectors" that understand the `%c` formatting
+  option. These are WebKit web inspectors, Firefox ([since version
+  31](https://hacks.mozilla.org/2014/05/editable-box-model-multiple-selection-sublime-text-keys-much-more-firefox-developer-tools-episode-31/))
+  and the Firebug plugin for Firefox (any version).
+
+  Colored output looks something like:
+
+  ![](https://cloud.githubusercontent.com/assets/71256/3139768/b98c5fd8-e8ef-11e3-862a-f7253b6f47c6.png)
+
+### stderr vs stdout
+
+You can set an alternative logging method per-namespace by overriding the `log` method on a per-namespace or globally:
+
+Example _stdout.js_:
+
+```js
+var debug = require('debug');
+var error = debug('app:error');
+
+// by default stderr is used
+error('goes to stderr!');
+
+var log = debug('app:log');
+// set this namespace to log via console.log
+log.log = console.log.bind(console); // don't forget to bind to console!
+log('goes to stdout');
+error('still goes to stderr!');
+
+// set all output to go via console.info
+// overrides all per-namespace log settings
+debug.log = console.info.bind(console);
+error('now goes to stdout via console.info');
+log('still goes to stdout, but via console.info now');
+```
+
+### Save debug output to a file
+
+You can save all debug statements to a file by piping them.
+
+Example:
+
+```bash
+$ DEBUG_FD=3 node your-app.js 3> whatever.log
+```
+
+## Authors
+
+ - TJ Holowaychuk
+ - Nathan Rajlich
+
+## License
+
+(The MIT License)
+
+Copyright (c) 2014 TJ Holowaychuk <tj@vision-media.ca>
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/cep/node_modules/debug/bower.json b/cep/node_modules/debug/bower.json
new file mode 100644
index 0000000..6af573f
--- /dev/null
+++ b/cep/node_modules/debug/bower.json
@@ -0,0 +1,28 @@
+{
+  "name": "visionmedia-debug",
+  "main": "dist/debug.js",
+  "version": "2.2.0",
+  "homepage": "https://github.com/visionmedia/debug",
+  "authors": [
+    "TJ Holowaychuk "
+  ],
+  "description": "visionmedia-debug",
+  "moduleType": [
+    "amd",
+    "es6",
+    "globals",
+    "node"
+  ],
+  "keywords": [
+    "visionmedia",
+    "debug"
+  ],
+  "license": "MIT",
+  "ignore": [
+    "**/.*",
+    "node_modules",
+    "bower_components",
+    "test",
+    "tests"
+  ]
+}
diff --git a/cep/node_modules/debug/browser.js b/cep/node_modules/debug/browser.js
new file mode 100644
index 0000000..7c76452
--- /dev/null
+++ b/cep/node_modules/debug/browser.js
@@ -0,0 +1,168 @@
+
+/**
+ * This is the web browser implementation of `debug()`.
+ *
+ * Expose `debug()` as the module.
+ */
+
+exports = module.exports = require('./debug');
+exports.log = log;
+exports.formatArgs = formatArgs;
+exports.save = save;
+exports.load = load;
+exports.useColors = useColors;
+exports.storage = 'undefined' != typeof chrome
+               && 'undefined' != typeof chrome.storage
+                  ? chrome.storage.local
+                  : localstorage();
+
+/**
+ * Colors.
+ */
+
+exports.colors = [
+  'lightseagreen',
+  'forestgreen',
+  'goldenrod',
+  'dodgerblue',
+  'darkorchid',
+  'crimson'
+];
+
+/**
+ * Currently only WebKit-based Web Inspectors, Firefox >= v31,
+ * and the Firebug extension (any Firefox version) are known
+ * to support "%c" CSS customizations.
+ *
+ * TODO: add a `localStorage` variable to explicitly enable/disable colors
+ */
+
+function useColors() {
+  // is webkit? http://stackoverflow.com/a/16459606/376773
+  return ('WebkitAppearance' in document.documentElement.style) ||
+    // is firebug? http://stackoverflow.com/a/398120/376773
+    (window.console && (console.firebug || (console.exception && console.table))) ||
+    // is firefox >= v31?
+    // https://developer.mozilla.org/en-US/docs/Tools/Web_Console#Styling_messages
+    (navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/) && parseInt(RegExp.$1, 10) >= 31);
+}
+
+/**
+ * Map %j to `JSON.stringify()`, since no Web Inspectors do that by default.
+ */
+
+exports.formatters.j = function(v) {
+  return JSON.stringify(v);
+};
+
+
+/**
+ * Colorize log arguments if enabled.
+ *
+ * @api public
+ */
+
+function formatArgs() {
+  var args = arguments;
+  var useColors = this.useColors;
+
+  args[0] = (useColors ? '%c' : '')
+    + this.namespace
+    + (useColors ? ' %c' : ' ')
+    + args[0]
+    + (useColors ? '%c ' : ' ')
+    + '+' + exports.humanize(this.diff);
+
+  if (!useColors) return args;
+
+  var c = 'color: ' + this.color;
+  args = [args[0], c, 'color: inherit'].concat(Array.prototype.slice.call(args, 1));
+
+  // the final "%c" is somewhat tricky, because there could be other
+  // arguments passed either before or after the %c, so we need to
+  // figure out the correct index to insert the CSS into
+  var index = 0;
+  var lastC = 0;
+  args[0].replace(/%[a-z%]/g, function(match) {
+    if ('%%' === match) return;
+    index++;
+    if ('%c' === match) {
+      // we only are interested in the *last* %c
+      // (the user may have provided their own)
+      lastC = index;
+    }
+  });
+
+  args.splice(lastC, 0, c);
+  return args;
+}
+
+/**
+ * Invokes `console.log()` when available.
+ * No-op when `console.log` is not a "function".
+ *
+ * @api public
+ */
+
+function log() {
+  // this hackery is required for IE8/9, where
+  // the `console.log` function doesn't have 'apply'
+  return 'object' === typeof console
+    && console.log
+    && Function.prototype.apply.call(console.log, console, arguments);
+}
+
+/**
+ * Save `namespaces`.
+ *
+ * @param {String} namespaces
+ * @api private
+ */
+
+function save(namespaces) {
+  try {
+    if (null == namespaces) {
+      exports.storage.removeItem('debug');
+    } else {
+      exports.storage.debug = namespaces;
+    }
+  } catch(e) {}
+}
+
+/**
+ * Load `namespaces`.
+ *
+ * @return {String} returns the previously persisted debug modes
+ * @api private
+ */
+
+function load() {
+  var r;
+  try {
+    r = exports.storage.debug;
+  } catch(e) {}
+  return r;
+}
+
+/**
+ * Enable namespaces listed in `localStorage.debug` initially.
+ */
+
+exports.enable(load());
+
+/**
+ * Localstorage attempts to return the localstorage.
+ *
+ * This is necessary because safari throws
+ * when a user disables cookies/localstorage
+ * and you attempt to access it.
+ *
+ * @return {LocalStorage}
+ * @api private
+ */
+
+function localstorage(){
+  try {
+    return window.localStorage;
+  } catch (e) {}
+}
diff --git a/cep/node_modules/debug/component.json b/cep/node_modules/debug/component.json
new file mode 100644
index 0000000..ca10637
--- /dev/null
+++ b/cep/node_modules/debug/component.json
@@ -0,0 +1,19 @@
+{
+  "name": "debug",
+  "repo": "visionmedia/debug",
+  "description": "small debugging utility",
+  "version": "2.2.0",
+  "keywords": [
+    "debug",
+    "log",
+    "debugger"
+  ],
+  "main": "browser.js",
+  "scripts": [
+    "browser.js",
+    "debug.js"
+  ],
+  "dependencies": {
+    "rauchg/ms.js": "0.7.1"
+  }
+}
diff --git a/cep/node_modules/debug/debug.js b/cep/node_modules/debug/debug.js
new file mode 100644
index 0000000..7571a86
--- /dev/null
+++ b/cep/node_modules/debug/debug.js
@@ -0,0 +1,197 @@
+
+/**
+ * This is the common logic for both the Node.js and web browser
+ * implementations of `debug()`.
+ *
+ * Expose `debug()` as the module.
+ */
+
+exports = module.exports = debug;
+exports.coerce = coerce;
+exports.disable = disable;
+exports.enable = enable;
+exports.enabled = enabled;
+exports.humanize = require('ms');
+
+/**
+ * The currently active debug mode names, and names to skip.
+ */
+
+exports.names = [];
+exports.skips = [];
+
+/**
+ * Map of special "%n" handling functions, for the debug "format" argument.
+ *
+ * Valid key names are a single, lowercased letter, i.e. "n".
+ */
+
+exports.formatters = {};
+
+/**
+ * Previously assigned color.
+ */
+
+var prevColor = 0;
+
+/**
+ * Previous log timestamp.
+ */
+
+var prevTime;
+
+/**
+ * Select a color.
+ *
+ * @return {Number}
+ * @api private
+ */
+
+function selectColor() {
+  return exports.colors[prevColor++ % exports.colors.length];
+}
+
+/**
+ * Create a debugger with the given `namespace`.
+ *
+ * @param {String} namespace
+ * @return {Function}
+ * @api public
+ */
+
+function debug(namespace) {
+
+  // define the `disabled` version
+  function disabled() {
+  }
+  disabled.enabled = false;
+
+  // define the `enabled` version
+  function enabled() {
+
+    var self = enabled;
+
+    // set `diff` timestamp
+    var curr = +new Date();
+    var ms = curr - (prevTime || curr);
+    self.diff = ms;
+    self.prev = prevTime;
+    self.curr = curr;
+    prevTime = curr;
+
+    // add the `color` if not set
+    if (null == self.useColors) self.useColors = exports.useColors();
+    if (null == self.color && self.useColors) self.color = selectColor();
+
+    var args = Array.prototype.slice.call(arguments);
+
+    args[0] = exports.coerce(args[0]);
+
+    if ('string' !== typeof args[0]) {
+      // anything else let's inspect with %o
+      args = ['%o'].concat(args);
+    }
+
+    // apply any `formatters` transformations
+    var index = 0;
+    args[0] = args[0].replace(/%([a-z%])/g, function(match, format) {
+      // if we encounter an escaped % then don't increase the array index
+      if (match === '%%') return match;
+      index++;
+      var formatter = exports.formatters[format];
+      if ('function' === typeof formatter) {
+        var val = args[index];
+        match = formatter.call(self, val);
+
+        // now we need to remove `args[index]` since it's inlined in the `format`
+        args.splice(index, 1);
+        index--;
+      }
+      return match;
+    });
+
+    if ('function' === typeof exports.formatArgs) {
+      args = exports.formatArgs.apply(self, args);
+    }
+    var logFn = enabled.log || exports.log || console.log.bind(console);
+    logFn.apply(self, args);
+  }
+  enabled.enabled = true;
+
+  var fn = exports.enabled(namespace) ? enabled : disabled;
+
+  fn.namespace = namespace;
+
+  return fn;
+}
+
+/**
+ * Enables a debug mode by namespaces. This can include modes
+ * separated by a colon and wildcards.
+ *
+ * @param {String} namespaces
+ * @api public
+ */
+
+function enable(namespaces) {
+  exports.save(namespaces);
+
+  var split = (namespaces || '').split(/[\s,]+/);
+  var len = split.length;
+
+  for (var i = 0; i < len; i++) {
+    if (!split[i]) continue; // ignore empty strings
+    namespaces = split[i].replace(/\*/g, '.*?');
+    if (namespaces[0] === '-') {
+      exports.skips.push(new RegExp('^' + namespaces.substr(1) + '$'));
+    } else {
+      exports.names.push(new RegExp('^' + namespaces + '$'));
+    }
+  }
+}
+
+/**
+ * Disable debug output.
+ *
+ * @api public
+ */
+
+function disable() {
+  exports.enable('');
+}
+
+/**
+ * Returns true if the given mode name is enabled, false otherwise.
+ *
+ * @param {String} name
+ * @return {Boolean}
+ * @api public
+ */
+
+function enabled(name) {
+  var i, len;
+  for (i = 0, len = exports.skips.length; i < len; i++) {
+    if (exports.skips[i].test(name)) {
+      return false;
+    }
+  }
+  for (i = 0, len = exports.names.length; i < len; i++) {
+    if (exports.names[i].test(name)) {
+      return true;
+    }
+  }
+  return false;
+}
+
+/**
+ * Coerce `val`.
+ *
+ * @param {Mixed} val
+ * @return {Mixed}
+ * @api private
+ */
+
+function coerce(val) {
+  if (val instanceof Error) return val.stack || val.message;
+  return val;
+}
diff --git a/cep/node_modules/debug/node.js b/cep/node_modules/debug/node.js
new file mode 100644
index 0000000..1d392a8
--- /dev/null
+++ b/cep/node_modules/debug/node.js
@@ -0,0 +1,209 @@
+
+/**
+ * Module dependencies.
+ */
+
+var tty = require('tty');
+var util = require('util');
+
+/**
+ * This is the Node.js implementation of `debug()`.
+ *
+ * Expose `debug()` as the module.
+ */
+
+exports = module.exports = require('./debug');
+exports.log = log;
+exports.formatArgs = formatArgs;
+exports.save = save;
+exports.load = load;
+exports.useColors = useColors;
+
+/**
+ * Colors.
+ */
+
+exports.colors = [6, 2, 3, 4, 5, 1];
+
+/**
+ * The file descriptor to write the `debug()` calls to.
+ * Set the `DEBUG_FD` env variable to override with another value. i.e.:
+ *
+ *   $ DEBUG_FD=3 node script.js 3>debug.log
+ */
+
+var fd = parseInt(process.env.DEBUG_FD, 10) || 2;
+var stream = 1 === fd ? process.stdout :
+             2 === fd ? process.stderr :
+             createWritableStdioStream(fd);
+
+/**
+ * Is stdout a TTY? Colored output is enabled when `true`.
+ */
+
+function useColors() {
+  var debugColors = (process.env.DEBUG_COLORS || '').trim().toLowerCase();
+  if (0 === debugColors.length) {
+    return tty.isatty(fd);
+  } else {
+    return '0' !== debugColors
+        && 'no' !== debugColors
+        && 'false' !== debugColors
+        && 'disabled' !== debugColors;
+  }
+}
+
+/**
+ * Map %o to `util.inspect()`, since Node doesn't do that out of the box.
+ */
+
+var inspect = (4 === util.inspect.length ?
+  // node <= 0.8.x
+  function (v, colors) {
+    return util.inspect(v, void 0, void 0, colors);
+  } :
+  // node > 0.8.x
+  function (v, colors) {
+    return util.inspect(v, { colors: colors });
+  }
+);
+
+exports.formatters.o = function(v) {
+  return inspect(v, this.useColors)
+    .replace(/\s*\n\s*/g, ' ');
+};
+
+/**
+ * Adds ANSI color escape codes if enabled.
+ *
+ * @api public
+ */
+
+function formatArgs() {
+  var args = arguments;
+  var useColors = this.useColors;
+  var name = this.namespace;
+
+  if (useColors) {
+    var c = this.color;
+
+    args[0] = '  \u001b[3' + c + ';1m' + name + ' '
+      + '\u001b[0m'
+      + args[0] + '\u001b[3' + c + 'm'
+      + ' +' + exports.humanize(this.diff) + '\u001b[0m';
+  } else {
+    args[0] = new Date().toUTCString()
+      + ' ' + name + ' ' + args[0];
+  }
+  return args;
+}
+
+/**
+ * Invokes `console.error()` with the specified arguments.
+ */
+
+function log() {
+  return stream.write(util.format.apply(this, arguments) + '\n');
+}
+
+/**
+ * Save `namespaces`.
+ *
+ * @param {String} namespaces
+ * @api private
+ */
+
+function save(namespaces) {
+  if (null == namespaces) {
+    // If you set a process.env field to null or undefined, it gets cast to the
+    // string 'null' or 'undefined'. Just delete instead.
+    delete process.env.DEBUG;
+  } else {
+    process.env.DEBUG = namespaces;
+  }
+}
+
+/**
+ * Load `namespaces`.
+ *
+ * @return {String} returns the previously persisted debug modes
+ * @api private
+ */
+
+function load() {
+  return process.env.DEBUG;
+}
+
+/**
+ * Copied from `node/src/node.js`.
+ *
+ * XXX: It's lame that node doesn't expose this API out-of-the-box. It also
+ * relies on the undocumented `tty_wrap.guessHandleType()` which is also lame.
+ */
+
+function createWritableStdioStream (fd) {
+  var stream;
+  var tty_wrap = process.binding('tty_wrap');
+
+  // Note stream._type is used for test-module-load-list.js
+
+  switch (tty_wrap.guessHandleType(fd)) {
+    case 'TTY':
+      stream = new tty.WriteStream(fd);
+      stream._type = 'tty';
+
+      // Hack to have stream not keep the event loop alive.
+      // See https://github.com/joyent/node/issues/1726
+      if (stream._handle && stream._handle.unref) {
+        stream._handle.unref();
+      }
+      break;
+
+    case 'FILE':
+      var fs = require('fs');
+      stream = new fs.SyncWriteStream(fd, { autoClose: false });
+      stream._type = 'fs';
+      break;
+
+    case 'PIPE':
+    case 'TCP':
+      var net = require('net');
+      stream = new net.Socket({
+        fd: fd,
+        readable: false,
+        writable: true
+      });
+
+      // FIXME Should probably have an option in net.Socket to create a
+      // stream from an existing fd which is writable only. But for now
+      // we'll just add this hack and set the `readable` member to false.
+      // Test: ./node test/fixtures/echo.js < /etc/passwd
+      stream.readable = false;
+      stream.read = null;
+      stream._type = 'pipe';
+
+      // FIXME Hack to have stream not keep the event loop alive.
+      // See https://github.com/joyent/node/issues/1726
+      if (stream._handle && stream._handle.unref) {
+        stream._handle.unref();
+      }
+      break;
+
+    default:
+      // Probably an error on in uv_guess_handle()
+      throw new Error('Implement me. Unknown stream file type!');
+  }
+
+  // For supporting legacy API we put the FD here.
+  stream.fd = fd;
+
+  stream._isStdio = true;
+
+  return stream;
+}
+
+/**
+ * Enable namespaces listed in `process.env.DEBUG` initially.
+ */
+
+exports.enable(load());
diff --git a/cep/node_modules/debug/package.json b/cep/node_modules/debug/package.json
new file mode 100644
index 0000000..c88db88
--- /dev/null
+++ b/cep/node_modules/debug/package.json
@@ -0,0 +1,112 @@
+{
+  "_args": [
+    [
+      {
+        "raw": "debug@2.2.0",
+        "scope": null,
+        "escapedName": "debug",
+        "name": "debug",
+        "rawSpec": "2.2.0",
+        "spec": "2.2.0",
+        "type": "version"
+      },
+      "D:\\repositories\\tk-framework-adobe\\extensions\\http_server\\node_modules\\socket.io"
+    ]
+  ],
+  "_from": "debug@2.2.0",
+  "_id": "debug@2.2.0",
+  "_inCache": true,
+  "_installable": true,
+  "_location": "/debug",
+  "_nodeVersion": "0.12.2",
+  "_npmUser": {
+    "name": "tootallnate",
+    "email": "nathan@tootallnate.net"
+  },
+  "_npmVersion": "2.7.4",
+  "_phantomChildren": {},
+  "_requested": {
+    "raw": "debug@2.2.0",
+    "scope": null,
+    "escapedName": "debug",
+    "name": "debug",
+    "rawSpec": "2.2.0",
+    "spec": "2.2.0",
+    "type": "version"
+  },
+  "_requiredBy": [
+    "/engine.io",
+    "/engine.io-client",
+    "/socket.io",
+    "/socket.io-adapter",
+    "/socket.io-client",
+    "/socket.io-parser"
+  ],
+  "_resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz",
+  "_shasum": "f87057e995b1a1f6ae6a4960664137bc56f039da",
+  "_shrinkwrap": null,
+  "_spec": "debug@2.2.0",
+  "_where": "D:\\repositories\\tk-framework-adobe\\extensions\\http_server\\node_modules\\socket.io",
+  "author": {
+    "name": "TJ Holowaychuk",
+    "email": "tj@vision-media.ca"
+  },
+  "browser": "./browser.js",
+  "bugs": {
+    "url": "https://github.com/visionmedia/debug/issues"
+  },
+  "component": {
+    "scripts": {
+      "debug/index.js": "browser.js",
+      "debug/debug.js": "debug.js"
+    }
+  },
+  "contributors": [
+    {
+      "name": "Nathan Rajlich",
+      "email": "nathan@tootallnate.net",
+      "url": "http://n8.io"
+    }
+  ],
+  "dependencies": {
+    "ms": "0.7.1"
+  },
+  "description": "small debugging utility",
+  "devDependencies": {
+    "browserify": "9.0.3",
+    "mocha": "*"
+  },
+  "directories": {},
+  "dist": {
+    "shasum": "f87057e995b1a1f6ae6a4960664137bc56f039da",
+    "tarball": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz"
+  },
+  "gitHead": "b38458422b5aa8aa6d286b10dfe427e8a67e2b35",
+  "homepage": "https://github.com/visionmedia/debug",
+  "keywords": [
+    "debug",
+    "log",
+    "debugger"
+  ],
+  "license": "MIT",
+  "main": "./node.js",
+  "maintainers": [
+    {
+      "name": "tjholowaychuk",
+      "email": "tj@vision-media.ca"
+    },
+    {
+      "name": "tootallnate",
+      "email": "nathan@tootallnate.net"
+    }
+  ],
+  "name": "debug",
+  "optionalDependencies": {},
+  "readme": "ERROR: No README data found!",
+  "repository": {
+    "type": "git",
+    "url": "git://github.com/visionmedia/debug.git"
+  },
+  "scripts": {},
+  "version": "2.2.0"
+}
diff --git a/cep/node_modules/engine.io-client/.npmignore b/cep/node_modules/engine.io-client/.npmignore
new file mode 100644
index 0000000..3957910
--- /dev/null
+++ b/cep/node_modules/engine.io-client/.npmignore
@@ -0,0 +1,2 @@
+support/
+test
diff --git a/cep/node_modules/engine.io-client/.travis.yml b/cep/node_modules/engine.io-client/.travis.yml
new file mode 100644
index 0000000..3eb12a7
--- /dev/null
+++ b/cep/node_modules/engine.io-client/.travis.yml
@@ -0,0 +1,46 @@
+language: node_js
+sudo: false
+node_js:
+  - '0.10'
+  - '0.12'
+  - '4'
+notifications:
+  irc: "irc.freenode.org#socket.io"
+matrix:
+  include:
+  - node_js: '0.10'
+    env: BROWSER_NAME=chrome BROWSER_VERSION=latest
+  - node_js: '0.10'
+    env: BROWSER_NAME=safari BROWSER_VERSION=latest
+  - node_js: '0.10'
+    env: BROWSER_NAME=ie BROWSER_VERSION=6
+  - node_js: '0.10'
+    env: BROWSER_NAME=ie BROWSER_VERSION=7
+  - node_js: '0.10'
+    env: BROWSER_NAME=ie BROWSER_VERSION=8
+  - node_js: '0.10'
+    env: BROWSER_NAME=ie BROWSER_VERSION=9
+  - node_js: '0.10'
+    env: BROWSER_NAME=ie BROWSER_VERSION=10 BROWSER_PLATFORM="Windows 2012"
+  - node_js: '0.10'
+    env: BROWSER_NAME=ie BROWSER_VERSION=latest BROWSER_PLATFORM="Windows 2012"
+  - node_js: '0.10'
+    env: BROWSER_NAME=iphone BROWSER_VERSION=5.1
+  - node_js: '0.10'
+    env: BROWSER_NAME=iphone BROWSER_VERSION=6.1
+  - node_js: '0.10'
+    env: BROWSER_NAME=iphone BROWSER_VERSION=7.1
+  - node_js: '0.10'
+    env: BROWSER_NAME=iphone BROWSER_VERSION=8.4
+  - node_js: '0.10'
+    env: BROWSER_NAME=iphone BROWSER_VERSION=latest
+  - node_js: '0.10'
+    env: BROWSER_NAME=android BROWSER_VERSION=4.0
+  - node_js: '0.10'
+    env: BROWSER_NAME=android BROWSER_VERSION=4.1
+  - node_js: '0.10'
+    env: BROWSER_NAME=android BROWSER_VERSION=4.2
+  - node_js: '0.10'
+    env: BROWSER_NAME=android BROWSER_VERSION=4.3
+  - node_js: '0.10'
+    env: BROWSER_NAME=android BROWSER_VERSION=4.4
diff --git a/cep/node_modules/engine.io-client/.zuul.yml b/cep/node_modules/engine.io-client/.zuul.yml
new file mode 100644
index 0000000..15d6892
--- /dev/null
+++ b/cep/node_modules/engine.io-client/.zuul.yml
@@ -0,0 +1,6 @@
+ui: mocha-bdd
+server: ./test/support/server.js
+tunnel:
+  type: ngrok
+  authtoken: 6Aw8vTgcG5EvXdQywVvbh_3fMxvd4Q7dcL2caAHAFjV
+  proto: tcp
diff --git a/cep/node_modules/engine.io-client/History.md b/cep/node_modules/engine.io-client/History.md
new file mode 100644
index 0000000..7b1dc9b
--- /dev/null
+++ b/cep/node_modules/engine.io-client/History.md
@@ -0,0 +1,568 @@
+
+1.6.11 / 2016-06-23
+===================
+
+  * bump version
+
+1.6.10 / 2016-06-23
+===================
+
+  * bump version
+
+1.6.9 / 2016-05-02
+==================
+
+  * default `rejectUnauthorized` to `true`
+
+1.6.8 / 2016-01-25
+==================
+
+  * safely resolve `ws` module
+
+1.6.7 / 2016-01-10
+==================
+
+  * prevent `ws` from being added to the bundle
+  * added jsonp fix for when no `
+
+```
+
+### With browserify
+
+Engine.IO is a commonjs module, which means you can include it by using
+`require` on the browser and package using [browserify](http://browserify.org/):
+
+1. install the client package
+
+    ```bash
+    $ npm install engine.io-client
+    ```
+
+1. write your app code
+
+    ```js
+    var socket = require('engine.io-client')('ws://localhost');
+    socket.on('open', function(){
+      socket.on('message', function(data){});
+      socket.on('close', function(){});
+    });
+    ```
+
+1. build your app bundle
+
+    ```bash
+    $ browserify app.js > bundle.js
+    ```
+
+1. include on your page
+
+    ```html
+    
+    ```
+
+### Sending and receiving binary
+
+```html
+
+
+```
+
+### Node.JS
+
+Add `engine.io-client` to your `package.json` and then:
+
+```js
+var socket = require('engine.io-client')('ws://localhost');
+socket.on('open', function(){
+  socket.on('message', function(data){});
+  socket.on('close', function(){});
+});
+```
+
+### Node.js with certificates
+```js
+var opts = {
+  key: fs.readFileSync('test/fixtures/client.key'),
+  cert: fs.readFileSync('test/fixtures/client.crt'),
+  ca: fs.readFileSync('test/fixtures/ca.crt')
+};
+
+var socket = require('engine.io-client')('ws://localhost', opts);
+socket.on('open', function(){
+  socket.on('message', function(data){});
+  socket.on('close', function(){});
+});
+```
+
+### Node.js with extraHeaders
+```js
+var opts = {
+  extraHeaders: {
+    'X-Custom-Header-For-My-Project': 'my-secret-access-token',
+    'Cookie': 'user_session=NI2JlCKF90aE0sJZD9ZzujtdsUqNYSBYxzlTsvdSUe35ZzdtVRGqYFr0kdGxbfc5gUOkR9RGp20GVKza; path=/; expires=Tue, 07-Apr-2015 18:18:08 GMT; secure; HttpOnly'
+  }
+};
+
+var socket = require('engine.io-client')('ws://localhost', opts);
+socket.on('open', function(){
+  socket.on('message', function(data){});
+  socket.on('close', function(){});
+});
+```
+
+## Features
+
+- Lightweight
+- Runs on browser and node.js seamlessly
+- Transports are independent of `Engine`
+  - Easy to debug
+  - Easy to unit test
+- Runs inside HTML5 WebWorker
+- Can send and receive binary data
+  - Receives as ArrayBuffer or Blob when in browser, and Buffer or ArrayBuffer
+    in Node
+  - When XHR2 or WebSockets are used, binary is emitted directly. Otherwise
+    binary is encoded into base64 strings, and decoded when binary types are
+    supported.
+  - With browsers that don't support ArrayBuffer, an object { base64: true,
+    data: dataAsBase64String } is emitted on the `message` event.
+
+## API
+
+### Socket
+
+The client class. Mixes in [Emitter](http://github.com/component/emitter).
+Exposed as `eio` in the browser standalone build.
+
+#### Properties
+
+- `protocol` _(Number)_: protocol revision number
+- `binaryType` _(String)_ : can be set to 'arraybuffer' or 'blob' in browsers,
+  and `buffer` or `arraybuffer` in Node. Blob is only used in browser if it's
+  supported.
+
+#### Events
+
+- `open`
+  - Fired upon successful connection.
+- `message`
+  - Fired when data is received from the server.
+  - **Arguments**
+    - `String` | `ArrayBuffer`: utf-8 encoded data or ArrayBuffer containing
+      binary data
+- `close`
+  - Fired upon disconnection. In compliance with the WebSocket API spec, this event may be 
+    fired even if the `open` event does not occur (i.e. due to connection error or `close()`).
+- `error`
+  - Fired when an error occurs.
+- `flush`
+  - Fired upon completing a buffer flush
+- `drain`
+  - Fired after `drain` event of transport if writeBuffer is empty
+- `upgradeError`
+  - Fired if an error occurs with a transport we're trying to upgrade to.
+- `upgrade`
+  - Fired upon upgrade success, after the new transport is set
+- `ping`
+  - Fired upon _flushing_ a ping packet (ie: actual packet write out)
+- `pong`
+  - Fired upon receiving a pong packet.
+
+#### Methods
+
+- **constructor**
+    - Initializes the client
+    - **Parameters**
+      - `String` uri
+      - `Object`: optional, options object
+    - **Options**
+      - `agent` (`http.Agent`): `http.Agent` to use, defaults to `false` (NodeJS only)
+      - `upgrade` (`Boolean`): defaults to true, whether the client should try
+      to upgrade the transport from long-polling to something better.
+      - `forceJSONP` (`Boolean`): forces JSONP for polling transport.
+      - `jsonp` (`Boolean`): determines whether to use JSONP when
+        necessary for polling. If disabled (by settings to false) an error will
+        be emitted (saying "No transports available") if no other transports
+        are available. If another transport is available for opening a
+        connection (e.g. WebSocket) that transport
+        will be used instead.
+      - `forceBase64` (`Boolean`): forces base 64 encoding for polling transport even when XHR2 responseType is available and WebSocket even if the used standard supports binary.
+      - `enablesXDR` (`Boolean`): enables XDomainRequest for IE8 to avoid loading bar flashing with click sound. default to `false` because XDomainRequest has a flaw of not sending cookie.
+      - `timestampRequests` (`Boolean`): whether to add the timestamp with each
+        transport request. Note: polling requests are always stamped unless this
+        option is explicitly set to `false` (`false`)
+      - `timestampParam` (`String`): timestamp parameter (`t`)
+      - `policyPort` (`Number`): port the policy server listens on (`843`)
+      - `path` (`String`): path to connect to, default is `/engine.io`
+      - `transports` (`Array`): a list of transports to try (in order).
+      Defaults to `['polling', 'websocket']`. `Engine`
+      always attempts to connect directly with the first one, provided the
+      feature detection test for it passes.
+      - `rememberUpgrade` (`Boolean`): defaults to false.
+        If true and if the previous websocket connection to the server succeeded,
+        the connection attempt will bypass the normal upgrade process and will initially
+        try websocket. A connection attempt following a transport error will use the
+        normal upgrade process. It is recommended you turn this on only when using
+        SSL/TLS connections, or if you know that your network does not block websockets.
+      - `pfx` (`String`): Certificate, Private key and CA certificates to use for SSL. Can be used in Node.js client environment to manually specify certificate information.
+      - `key` (`String`): Private key to use for SSL. Can be used in Node.js client environment to manually specify certificate information.
+      - `passphrase` (`String`): A string of passphrase for the private key or pfx. Can be used in Node.js client environment to manually specify certificate information.
+      - `cert` (`String`): Public x509 certificate to use. Can be used in Node.js client environment to manually specify certificate information.
+      - `ca` (`String`|`Array`): An authority certificate or array of authority certificates to check the remote host against.. Can be used in Node.js client environment to manually specify certificate information.
+      - `ciphers` (`String`): A string describing the ciphers to use or exclude. Consult the [cipher format list](http://www.openssl.org/docs/apps/ciphers.html#CIPHER_LIST_FORMAT) for details on the format. Can be used in Node.js client environment to manually specify certificate information.
+      - `rejectUnauthorized` (`Boolean`): If true, the server certificate is verified against the list of supplied CAs. An 'error' event is emitted if verification fails. Verification happens at the connection level, before the HTTP request is sent. Can be used in Node.js client environment to manually specify certificate information.
+      - `perMessageDeflate` (`Object|Boolean`): parameters of the WebSocket permessage-deflate extension
+        (see [ws module](https://github.com/einaros/ws) api docs). Set to `false` to disable. (`true`)
+        - `threshold` (`Number`): data is compressed only if the byte size is above this value. This option is ignored on the browser. (`1024`)
+      - `extraHeaders` (`Object`): Headers that will be passed for each request to the server (via xhr-polling and via websockets). These values then can be used during handshake or for special proxies. Can only be used in Node.js client environment.
+- `send`
+    - Sends a message to the server
+    - **Parameters**
+      - `String` | `ArrayBuffer` | `ArrayBufferView` | `Blob`: data to send
+      - `Object`: optional, options object
+      - `Function`: optional, callback upon `drain`
+    - **Options**
+      - `compress` (`Boolean`): whether to compress sending data. This option is ignored and forced to be `true` on the browser. (`true`)
+- `close`
+    - Disconnects the client.
+
+### Transport
+
+The transport class. Private. _Inherits from EventEmitter_.
+
+#### Events
+
+- `poll`: emitted by polling transports upon starting a new request
+- `pollComplete`: emitted by polling transports upon completing a request
+- `drain`: emitted by polling transports upon a buffer drain
+
+## Tests
+
+`engine.io-client` is used to test
+[engine](http://github.com/socketio/engine.io). Running the `engine.io`
+test suite ensures the client works and vice-versa.
+
+Browser tests are run using [zuul](https://github.com/defunctzombie/zuul). You can
+run the tests locally using the following command.
+
+```
+./node_modules/.bin/zuul --local 8080 -- test/index.js
+```
+
+Additionally, `engine.io-client` has a standalone test suite you can run
+with `make test` which will run node.js and browser tests. You must have zuul setup with
+a saucelabs account.
+
+## Support
+
+The support channels for `engine.io-client` are the same as `socket.io`:
+  - irc.freenode.net **#socket.io**
+  - [Google Groups](http://groups.google.com/group/socket_io)
+  - [Website](http://socket.io)
+
+## Development
+
+To contribute patches, run tests or benchmarks, make sure to clone the
+repository:
+
+```bash
+git clone git://github.com/socketio/engine.io-client.git
+```
+
+Then:
+
+```bash
+cd engine.io-client
+npm install
+```
+
+See the `Tests` section above for how to run tests before submitting any patches.
+
+## License
+
+MIT - Copyright (c) 2014 Automattic, Inc.
+
diff --git a/cep/node_modules/engine.io-client/engine.io.js b/cep/node_modules/engine.io-client/engine.io.js
new file mode 100644
index 0000000..eeefaa9
--- /dev/null
+++ b/cep/node_modules/engine.io-client/engine.io.js
@@ -0,0 +1,4244 @@
+!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.eio=e()}}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o 0) {
+      this.extraHeaders = opts.extraHeaders;
+    }
+  }
+
+  this.open();
+}
+
+Socket.priorWebsocketSuccess = false;
+
+/**
+ * Mix in `Emitter`.
+ */
+
+Emitter(Socket.prototype);
+
+/**
+ * Protocol version.
+ *
+ * @api public
+ */
+
+Socket.protocol = parser.protocol; // this is an int
+
+/**
+ * Expose deps for legacy compatibility
+ * and standalone browser access.
+ */
+
+Socket.Socket = Socket;
+Socket.Transport = _dereq_('./transport');
+Socket.transports = _dereq_('./transports');
+Socket.parser = _dereq_('engine.io-parser');
+
+/**
+ * Creates transport of the given type.
+ *
+ * @param {String} transport name
+ * @return {Transport}
+ * @api private
+ */
+
+Socket.prototype.createTransport = function (name) {
+  debug('creating transport "%s"', name);
+  var query = clone(this.query);
+
+  // append engine.io protocol identifier
+  query.EIO = parser.protocol;
+
+  // transport name
+  query.transport = name;
+
+  // session id if we already have one
+  if (this.id) query.sid = this.id;
+
+  var transport = new transports[name]({
+    agent: this.agent,
+    hostname: this.hostname,
+    port: this.port,
+    secure: this.secure,
+    path: this.path,
+    query: query,
+    forceJSONP: this.forceJSONP,
+    jsonp: this.jsonp,
+    forceBase64: this.forceBase64,
+    enablesXDR: this.enablesXDR,
+    timestampRequests: this.timestampRequests,
+    timestampParam: this.timestampParam,
+    policyPort: this.policyPort,
+    socket: this,
+    pfx: this.pfx,
+    key: this.key,
+    passphrase: this.passphrase,
+    cert: this.cert,
+    ca: this.ca,
+    ciphers: this.ciphers,
+    rejectUnauthorized: this.rejectUnauthorized,
+    perMessageDeflate: this.perMessageDeflate,
+    extraHeaders: this.extraHeaders
+  });
+
+  return transport;
+};
+
+function clone (obj) {
+  var o = {};
+  for (var i in obj) {
+    if (obj.hasOwnProperty(i)) {
+      o[i] = obj[i];
+    }
+  }
+  return o;
+}
+
+/**
+ * Initializes transport to use and starts probe.
+ *
+ * @api private
+ */
+Socket.prototype.open = function () {
+  var transport;
+  if (this.rememberUpgrade && Socket.priorWebsocketSuccess && this.transports.indexOf('websocket') != -1) {
+    transport = 'websocket';
+  } else if (0 === this.transports.length) {
+    // Emit error on next tick so it can be listened to
+    var self = this;
+    setTimeout(function() {
+      self.emit('error', 'No transports available');
+    }, 0);
+    return;
+  } else {
+    transport = this.transports[0];
+  }
+  this.readyState = 'opening';
+
+  // Retry with the next transport if the transport is disabled (jsonp: false)
+  try {
+    transport = this.createTransport(transport);
+  } catch (e) {
+    this.transports.shift();
+    this.open();
+    return;
+  }
+
+  transport.open();
+  this.setTransport(transport);
+};
+
+/**
+ * Sets the current transport. Disables the existing one (if any).
+ *
+ * @api private
+ */
+
+Socket.prototype.setTransport = function(transport){
+  debug('setting transport %s', transport.name);
+  var self = this;
+
+  if (this.transport) {
+    debug('clearing existing transport %s', this.transport.name);
+    this.transport.removeAllListeners();
+  }
+
+  // set up transport
+  this.transport = transport;
+
+  // set up transport listeners
+  transport
+  .on('drain', function(){
+    self.onDrain();
+  })
+  .on('packet', function(packet){
+    self.onPacket(packet);
+  })
+  .on('error', function(e){
+    self.onError(e);
+  })
+  .on('close', function(){
+    self.onClose('transport close');
+  });
+};
+
+/**
+ * Probes a transport.
+ *
+ * @param {String} transport name
+ * @api private
+ */
+
+Socket.prototype.probe = function (name) {
+  debug('probing transport "%s"', name);
+  var transport = this.createTransport(name, { probe: 1 })
+    , failed = false
+    , self = this;
+
+  Socket.priorWebsocketSuccess = false;
+
+  function onTransportOpen(){
+    if (self.onlyBinaryUpgrades) {
+      var upgradeLosesBinary = !this.supportsBinary && self.transport.supportsBinary;
+      failed = failed || upgradeLosesBinary;
+    }
+    if (failed) return;
+
+    debug('probe transport "%s" opened', name);
+    transport.send([{ type: 'ping', data: 'probe' }]);
+    transport.once('packet', function (msg) {
+      if (failed) return;
+      if ('pong' == msg.type && 'probe' == msg.data) {
+        debug('probe transport "%s" pong', name);
+        self.upgrading = true;
+        self.emit('upgrading', transport);
+        if (!transport) return;
+        Socket.priorWebsocketSuccess = 'websocket' == transport.name;
+
+        debug('pausing current transport "%s"', self.transport.name);
+        self.transport.pause(function () {
+          if (failed) return;
+          if ('closed' == self.readyState) return;
+          debug('changing transport and sending upgrade packet');
+
+          cleanup();
+
+          self.setTransport(transport);
+          transport.send([{ type: 'upgrade' }]);
+          self.emit('upgrade', transport);
+          transport = null;
+          self.upgrading = false;
+          self.flush();
+        });
+      } else {
+        debug('probe transport "%s" failed', name);
+        var err = new Error('probe error');
+        err.transport = transport.name;
+        self.emit('upgradeError', err);
+      }
+    });
+  }
+
+  function freezeTransport() {
+    if (failed) return;
+
+    // Any callback called by transport should be ignored since now
+    failed = true;
+
+    cleanup();
+
+    transport.close();
+    transport = null;
+  }
+
+  //Handle any error that happens while probing
+  function onerror(err) {
+    var error = new Error('probe error: ' + err);
+    error.transport = transport.name;
+
+    freezeTransport();
+
+    debug('probe transport "%s" failed because of error: %s', name, err);
+
+    self.emit('upgradeError', error);
+  }
+
+  function onTransportClose(){
+    onerror("transport closed");
+  }
+
+  //When the socket is closed while we're probing
+  function onclose(){
+    onerror("socket closed");
+  }
+
+  //When the socket is upgraded while we're probing
+  function onupgrade(to){
+    if (transport && to.name != transport.name) {
+      debug('"%s" works - aborting "%s"', to.name, transport.name);
+      freezeTransport();
+    }
+  }
+
+  //Remove all listeners on the transport and on self
+  function cleanup(){
+    transport.removeListener('open', onTransportOpen);
+    transport.removeListener('error', onerror);
+    transport.removeListener('close', onTransportClose);
+    self.removeListener('close', onclose);
+    self.removeListener('upgrading', onupgrade);
+  }
+
+  transport.once('open', onTransportOpen);
+  transport.once('error', onerror);
+  transport.once('close', onTransportClose);
+
+  this.once('close', onclose);
+  this.once('upgrading', onupgrade);
+
+  transport.open();
+
+};
+
+/**
+ * Called when connection is deemed open.
+ *
+ * @api public
+ */
+
+Socket.prototype.onOpen = function () {
+  debug('socket open');
+  this.readyState = 'open';
+  Socket.priorWebsocketSuccess = 'websocket' == this.transport.name;
+  this.emit('open');
+  this.flush();
+
+  // we check for `readyState` in case an `open`
+  // listener already closed the socket
+  if ('open' == this.readyState && this.upgrade && this.transport.pause) {
+    debug('starting upgrade probes');
+    for (var i = 0, l = this.upgrades.length; i < l; i++) {
+      this.probe(this.upgrades[i]);
+    }
+  }
+};
+
+/**
+ * Handles a packet.
+ *
+ * @api private
+ */
+
+Socket.prototype.onPacket = function (packet) {
+  if ('opening' == this.readyState || 'open' == this.readyState) {
+    debug('socket receive: type "%s", data "%s"', packet.type, packet.data);
+
+    this.emit('packet', packet);
+
+    // Socket is live - any packet counts
+    this.emit('heartbeat');
+
+    switch (packet.type) {
+      case 'open':
+        this.onHandshake(parsejson(packet.data));
+        break;
+
+      case 'pong':
+        this.setPing();
+        this.emit('pong');
+        break;
+
+      case 'error':
+        var err = new Error('server error');
+        err.code = packet.data;
+        this.onError(err);
+        break;
+
+      case 'message':
+        this.emit('data', packet.data);
+        this.emit('message', packet.data);
+        break;
+    }
+  } else {
+    debug('packet received with socket readyState "%s"', this.readyState);
+  }
+};
+
+/**
+ * Called upon handshake completion.
+ *
+ * @param {Object} handshake obj
+ * @api private
+ */
+
+Socket.prototype.onHandshake = function (data) {
+  this.emit('handshake', data);
+  this.id = data.sid;
+  this.transport.query.sid = data.sid;
+  this.upgrades = this.filterUpgrades(data.upgrades);
+  this.pingInterval = data.pingInterval;
+  this.pingTimeout = data.pingTimeout;
+  this.onOpen();
+  // In case open handler closes socket
+  if  ('closed' == this.readyState) return;
+  this.setPing();
+
+  // Prolong liveness of socket on heartbeat
+  this.removeListener('heartbeat', this.onHeartbeat);
+  this.on('heartbeat', this.onHeartbeat);
+};
+
+/**
+ * Resets ping timeout.
+ *
+ * @api private
+ */
+
+Socket.prototype.onHeartbeat = function (timeout) {
+  clearTimeout(this.pingTimeoutTimer);
+  var self = this;
+  self.pingTimeoutTimer = setTimeout(function () {
+    if ('closed' == self.readyState) return;
+    self.onClose('ping timeout');
+  }, timeout || (self.pingInterval + self.pingTimeout));
+};
+
+/**
+ * Pings server every `this.pingInterval` and expects response
+ * within `this.pingTimeout` or closes connection.
+ *
+ * @api private
+ */
+
+Socket.prototype.setPing = function () {
+  var self = this;
+  clearTimeout(self.pingIntervalTimer);
+  self.pingIntervalTimer = setTimeout(function () {
+    debug('writing ping packet - expecting pong within %sms', self.pingTimeout);
+    self.ping();
+    self.onHeartbeat(self.pingTimeout);
+  }, self.pingInterval);
+};
+
+/**
+* Sends a ping packet.
+*
+* @api private
+*/
+
+Socket.prototype.ping = function () {
+  var self = this;
+  this.sendPacket('ping', function(){
+    self.emit('ping');
+  });
+};
+
+/**
+ * Called on `drain` event
+ *
+ * @api private
+ */
+
+Socket.prototype.onDrain = function() {
+  this.writeBuffer.splice(0, this.prevBufferLen);
+
+  // setting prevBufferLen = 0 is very important
+  // for example, when upgrading, upgrade packet is sent over,
+  // and a nonzero prevBufferLen could cause problems on `drain`
+  this.prevBufferLen = 0;
+
+  if (0 === this.writeBuffer.length) {
+    this.emit('drain');
+  } else {
+    this.flush();
+  }
+};
+
+/**
+ * Flush write buffers.
+ *
+ * @api private
+ */
+
+Socket.prototype.flush = function () {
+  if ('closed' != this.readyState && this.transport.writable &&
+    !this.upgrading && this.writeBuffer.length) {
+    debug('flushing %d packets in socket', this.writeBuffer.length);
+    this.transport.send(this.writeBuffer);
+    // keep track of current length of writeBuffer
+    // splice writeBuffer and callbackBuffer on `drain`
+    this.prevBufferLen = this.writeBuffer.length;
+    this.emit('flush');
+  }
+};
+
+/**
+ * Sends a message.
+ *
+ * @param {String} message.
+ * @param {Function} callback function.
+ * @param {Object} options.
+ * @return {Socket} for chaining.
+ * @api public
+ */
+
+Socket.prototype.write =
+Socket.prototype.send = function (msg, options, fn) {
+  this.sendPacket('message', msg, options, fn);
+  return this;
+};
+
+/**
+ * Sends a packet.
+ *
+ * @param {String} packet type.
+ * @param {String} data.
+ * @param {Object} options.
+ * @param {Function} callback function.
+ * @api private
+ */
+
+Socket.prototype.sendPacket = function (type, data, options, fn) {
+  if('function' == typeof data) {
+    fn = data;
+    data = undefined;
+  }
+
+  if ('function' == typeof options) {
+    fn = options;
+    options = null;
+  }
+
+  if ('closing' == this.readyState || 'closed' == this.readyState) {
+    return;
+  }
+
+  options = options || {};
+  options.compress = false !== options.compress;
+
+  var packet = {
+    type: type,
+    data: data,
+    options: options
+  };
+  this.emit('packetCreate', packet);
+  this.writeBuffer.push(packet);
+  if (fn) this.once('flush', fn);
+  this.flush();
+};
+
+/**
+ * Closes the connection.
+ *
+ * @api private
+ */
+
+Socket.prototype.close = function () {
+  if ('opening' == this.readyState || 'open' == this.readyState) {
+    this.readyState = 'closing';
+
+    var self = this;
+
+    if (this.writeBuffer.length) {
+      this.once('drain', function() {
+        if (this.upgrading) {
+          waitForUpgrade();
+        } else {
+          close();
+        }
+      });
+    } else if (this.upgrading) {
+      waitForUpgrade();
+    } else {
+      close();
+    }
+  }
+
+  function close() {
+    self.onClose('forced close');
+    debug('socket closing - telling transport to close');
+    self.transport.close();
+  }
+
+  function cleanupAndClose() {
+    self.removeListener('upgrade', cleanupAndClose);
+    self.removeListener('upgradeError', cleanupAndClose);
+    close();
+  }
+
+  function waitForUpgrade() {
+    // wait for upgrade to finish since we can't send packets while pausing a transport
+    self.once('upgrade', cleanupAndClose);
+    self.once('upgradeError', cleanupAndClose);
+  }
+
+  return this;
+};
+
+/**
+ * Called upon transport error
+ *
+ * @api private
+ */
+
+Socket.prototype.onError = function (err) {
+  debug('socket error %j', err);
+  Socket.priorWebsocketSuccess = false;
+  this.emit('error', err);
+  this.onClose('transport error', err);
+};
+
+/**
+ * Called upon transport close.
+ *
+ * @api private
+ */
+
+Socket.prototype.onClose = function (reason, desc) {
+  if ('opening' == this.readyState || 'open' == this.readyState || 'closing' == this.readyState) {
+    debug('socket close with reason: "%s"', reason);
+    var self = this;
+
+    // clear timers
+    clearTimeout(this.pingIntervalTimer);
+    clearTimeout(this.pingTimeoutTimer);
+
+    // stop event from firing again for transport
+    this.transport.removeAllListeners('close');
+
+    // ensure transport won't stay open
+    this.transport.close();
+
+    // ignore further transport communication
+    this.transport.removeAllListeners();
+
+    // set ready state
+    this.readyState = 'closed';
+
+    // clear session id
+    this.id = null;
+
+    // emit close event
+    this.emit('close', reason, desc);
+
+    // clean buffers after, so users can still
+    // grab the buffers on `close` event
+    self.writeBuffer = [];
+    self.prevBufferLen = 0;
+  }
+};
+
+/**
+ * Filters upgrades, returning only those matching client transports.
+ *
+ * @param {Array} server upgrades
+ * @api private
+ *
+ */
+
+Socket.prototype.filterUpgrades = function (upgrades) {
+  var filteredUpgrades = [];
+  for (var i = 0, j = upgrades.length; i